Search code examples
javascriptevent-handlingdom-eventsflush

Can I flush the event stack within Firefox using Javascript?


I have a hierarchy of tags within my HTML which all contain onclick event handlers. The onclick is pushed onto the event stack from the leaf back through the root of the hierarchy. I only want to respond to the leaf onclick event. Can I flush the event stack rather than using a flag?

For instance:

    <ul>
      <li onclick="nada('1');"><a href="#1">1</a></li>
      <li onclick="nada('2');"><a href="#2">2</a>
        <ul>
          <li onclick="nada('2.1');"><a href="#2.1">2.1</a></li>
          <li onclick="nada('2.2');"><a href="#2.2">2.2</a></li>
          <li onclick="nada('2.3');"><a href="#2.3">2.3</a></li>
        </ul>
      </li>
      <li onclick="nada('4');"><a href="#4">4</a></li>
      <li onclick="nada('5');"><a href="#5">5</a></li>
    </ul>

Clicking on 2.2 using this function...

function nada(which)
  {
  alert(which);
  }

...will result in two alerts for '2.2' and '2'.

What could I add to the nada function to eliminate the alert for '2'?


Solution

  • To stop the event bubbling up to parent elements you have to tell the event object about it. In IE, you set event.cancelBubble= true. In other browsers, you call event.stopPropagation().

    You probably also want to turn off the default link-following action so that the browser doesn't keep jumping up to the top trying to follow the non-existing anchor links like #1. In IE, you set event.returnValue= false. In other browsers, you call event.preventDefault().

    The event object is accessible as window.event on IE. On other browsers, it is passed into the event handler function. A way to pass the event into a function that works on both is:

          <li onclick="nada('2.1', event);"><a href="#2.1">2.1</a></li>
    
    function nada(n, event) {
        alert(n);
        if ('stopPropagation' in event) {
            event.stopPropagation();
            event.preventDefault();
        } else {
            event.cancelBubble= true;
            event.returnValue= false;
        }
    }
    

    However it would probably be better all round to put the onclick event on the a element which it usually belongs. This helps for accessibility, as the a element will be focusable and keyboard-operable. And it means you don't have to worry about parents' click handlers being called.

    (You can style the a to look like a plain block, if you want.)

    You can then also kick out the redundant onclick links with a bit of unobtrusive scripting:

    <ul id="nadalist">
      <li><a href="#1">1</a></li>
      <li><a href="#2">2</a>
          <ul>
              <li><a href="#2.1">2.1</a></li>
              <li><a href="#2.2">2.2</a></li>
              <li><a href="#2.3">2.3</a></li>
          </ul>
      </li>
      <li><a href="#4">4</a></li>
      <li><a href="#5">5</a></li>
    </ul>
    
    <script type="text/javascript">
        var links= document.getElementById('nadalist').getElementsByTagName('a');
        for (var i= links.length; i-->0;) {
            links[i].onclick= function() {
                alert(this.hash.substring(1));
                return false;
            }
        }
    </script>