Barney
Barney

Reputation: 16456

Capture only hashchange events not resulting from anchor clicks

I am trying to use Javascript to emulate the CSS :target pseudo-class so as to capture all events that result in an element on page being targeted. I've identified 3 trigger events:

  1. window.location.hash already targets an element of the same ID on initialisation
  2. An anchor targeting the element is clicked
  3. The hashchange event is fired independently of the above (for example via the window.history API)

Scenario 2 is important as a distinct case since I would want to invoke the click event's preventDefault. The simplified code for this scenario follows:

$('body').on('click', 'a[href*=#]', function filterTarget(clickEvent){
    $(this.hash).trigger('target', [clickEvent]);
});

The problem comes when trying to implement scenario 3:

$(window).on('hashchange', function filterTarget(hashChangeEvent){
    $(this.hash).trigger('target', [hashChangeEvent]);
});

If a target handler doesn't cancel the native behaviour for scenario 2, it will be triggered again when the native behaviour causes the resulting hashchange event. How can I filter out these edge cases?

POST-SOLUTION EDIT:

roasted's answer held the key — handle a namespaced hashchange event, then unbind and rebind the handler based on logic handled inside the click handler and its preventDefault. I wrote up the full plugin here.

Upvotes: 4

Views: 2770

Answers (3)

A. Wolff
A. Wolff

Reputation: 74420

If i understand it, you don't want the hashchange event to be fired if an anchor tag is clicked. You could then set your logic using namespaced events:

DEMO

$('body').on('click', 'a[href*=#]', function (clickEvent) {
    filterTarget(clickEvent,this);  
    $(window).off('hashchange.filter').on('hashchange.tmp', function () {
          $(this).off('hashchange.tmp').on('hashchange.filter', filterTarget);
    });
});
$(window).on('hashchange.filter', filterTarget);

function filterTarget(event,elem) {
    $(elem?elem.hash:window.location.hash).trigger('target', [event]);
    //you could filter depending event.type
    alert(event.type + '::'+ (elem?elem.hash:window.location.hash));
}

Upvotes: 5

dandavis
dandavis

Reputation: 16726

if the click is setting the hash with the fragment anyway, just throw away duplicates in the hash change event:

onhashchange=function(e){
  if(e.newURL == e.oldURL ){return; }
 //do your normal hashchange event stuff below:

};

ref: https://developer.mozilla.org/en-US/docs/Web/API/window.onhashchange

this fixes cascade issues no matter what invoked the change.

Upvotes: 1

vaporbook
vaporbook

Reputation: 275

Seems like you could use mousedown instead of click, if you're going to be calling preventDefault on it. Then presumably the hashchange would not be triggered.

Upvotes: 0

Related Questions