views:

122

answers:

4

I find myself very often in the situation that I open an element in a web page - e.g. a drop-down menu - that I want to close if the user clicks anywhere on the page except the element itself.

To keep things simple, I have mostly written the code myself instead of employing some drop-down menu class.

However, I have never managed to build an implementation of this that was completely satisfying: Event handling and bubbling would work differently in different browsers, there would be the need for nasty workarounds, in some situations clicking the drop-down button would start closing it in the same moment, and so on.

Is there a Prototype based, authoritative, best practice to do this? Something that works across browsers - IE6 being a plus but not a requirement?

Just this:

  • click on a button - an element opens (e.g. an absolutely positioned drop-down menu).
  • click within the element - the element stays open.
  • click on the button that opened the element - the element stays open.
  • click anywhere else on the page - the element closes.

I need help with the event handling part only, the displaying of the menu is totally secondary.

+1  A: 

AFAIK, you need to make an invisible div the size of window, put it behind the current element, and add a click event to that.

Skilldrick
That is a viable approach, but I would like to avoid it because then you can't immediately click something else on the page. There is always one click that is required solely to close the element.
Pekka
So when you say "click anywhere else on the page - the element closes." you mean you want the default action clicking on that point to be carried out?
Skilldrick
You could do something really hacky like work out the position of the click and then... No, that's a really bad idea.
Skilldrick
A: 

Just thinking out loud but you might be able to use the blur event on the dropdown to hide it (blur gets fired when an element loses focus) or another idea might be when the dropdown opens attach a click event to the document object that hides the dropdown. Events get propagated through their containers so it should end up at the document object eventually. You might need to call preventPropegation on the event when your dropdown gets clicked so that it doesn't make it to the handler attached to the document.

John Duff
Good idea but the element does not necessary need to be focused when opened. A working solution to the event propagation issue is what I'm looking for, though, just in connection with onClick.
Pekka
A: 

maybe you could calculate the Position (X,Y) for the clickevent and compare that to the cumulativeOffset (cumulativeScrollOffset) + [el.width|el.height] of the desired container.

Event.observe(window, 'click', function(e) {

  var el = $('el')

  if( el.cumulativeOffset[0] < e.Event.pointerX(e) ...  )

});

<div id="el" style="position:absolute;width:100px;height:100px;background-color:#00F;top:100px;left:300px;">

</div>
nils petersohn
Creative idea, but has too many things that can go wrong I think. I think it needs to be event driven to be really safe.
Pekka
+3  A: 
Event.observe(document, 'click', function (event) {
  switch (event.element().id) {
    case 'example_id':
      // do different stuff depending on element clicked
      // ofc u don't need to pass id, u can simply throw an element itself
      break;
    default:
      // do close action
      break;
  }
  // also check Event.findElement();
});

You can also add specific classes to the items you don't want to trigger close action and check it inside

if (!event.element().hasClassName('dont_close'))   
  Element.remove(selectDOMElement);
Tomasz Durka