最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Capture only hashchange events not resulting from anchor clicks - Stack Overflow

programmeradmin1浏览0评论

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 es 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.

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 es 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.

Share Improve this question edited Aug 4, 2013 at 19:11 I. J. Kennedy 25.9k17 gold badges65 silver badges88 bronze badges asked Jul 3, 2013 at 15:17 BarneyBarney 16.5k5 gold badges65 silver badges80 bronze badges 5
  • Could you give an example of how a user might use the system you're describing? – Samuel Reid Commented Jul 3, 2013 at 15:31
  • @SamuelReid I'm working on a rich single-page web app with a strong focus on progressive enhancement: all interactions and navigations that are conceptually feasible for user agents without Javascript are handled with form elements, and (most often) links — a lot of the time hashes are used to navigate to or reveal elements within the page. But when Javascript is applied, I would often want to interrupt those events and replace native behaviour. – Barney Commented Jul 3, 2013 at 15:33
  • @SamuelReid for a specific example: there is something like a 'footnotes viewer' that displays extensive citations. Native behaviour is to simply jump to the citation in question, but enhanced behaviour opens up a modal interface to better interact with citations and source material references holistically. I want people to be able to share permalinks to footnotes, but if JS is available I still want the 'footnotes viewer' where possible. – Barney Commented Jul 3, 2013 at 15:36
  • So you need it so that if scenario 2 does not cancel the default event, scenario 3 will run? – Samuel Reid Commented Jul 3, 2013 at 15:51
  • @SamuelReid no — I'm saying that a hashchange resulting from a click should not trigger another target event, because in holistic terms that targeting event has already been captured. – Barney Commented Jul 3, 2013 at 16:19
Add a ment  | 

3 Answers 3

Reset to default 5 +200

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

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/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.

发布评论

评论列表(0)

  1. 暂无评论