Question

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.

Was it helpful?

Solution

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));
}

OTHER TIPS

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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top