overlay.onclick = function(e){
e.preventDefault();
window.location.hash = 'overlay';
var close = function(){
//do some stuff
window.removeEventListener('hashchange', close);
}
window.addEventListener('hashchange', close, false);
}
Basically, as soon as I click the link, the hash is updated, and the close function is calling. The close function shouldn't be bound until AFTER the hash is changed. Why is the close function being called as soon as the listener is added, and how do I prevent it. Testing in Chrome, latest version.
I think it's because Javascript is synchronous, so when you set window.location.hash, the window.onhashchange method will not run until the onclick function currently running finishes. Does that make sense? So you set the .hash value, then bind a hashchange event...right after binding, the onhashchange event actually fires. So that in turn calls close. Try putting console.log statements throughout your code to see the order of execution.
UPDATE:
Here's a fiddle to demonstrate the order of things: http://jsfiddle.net/jmWDY/
Notice how the original onhashchange function is called first (after your function is finished), then your new binding (which is close). I hope this helped!
Related
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:
window.location.hash already targets an element of the same ID on initialisation
An anchor targeting the element is clicked
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.
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.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.
I'm using the one() function in jQuery to prevent multiple clicks. However, when they click the element a second time, it does the annoying click jump and sends you back to the top of the page.
Is there a way to prevent this from happening? Unbinding the click and re-binding it when the function is done has the same result (and I'm assuming that one() just unbinds the event anyways).
A quick example of this happening: http://jsfiddle.net/iwasrobbed/FtbZa/
I'm not sure if this is better or not, but you could bind a simple function that maintains the return false.
jQuery provides a shortcut for this with .bind('click',false).
$('.someLink').one('click', function() {
$(this).bind('click',false);
return false;
});
or if you have several of these links, a very efficient alternative would be to use the delegate()[docs] method to bind a handler to a common ancestor that takes care of the return false; for you.
Here I just used the body, but you could use a nearer ancestor.
$('.someLink').one('click', function() {
});
$('body').delegate('.someLink','click',function(){return false;});
Try changing the href so the '#' isn't being used: http://jsfiddle.net/FtbZa/1/
$('.someLink').one('click', function() {
alert('test');
return false;
}).attr('href', 'javascript:void(0);');
You could use the standard .click() function and a little logic:
1. $('.someLink').click(function(event) {
2. event.preventDefault();
3. if (!$(this).hasClass("clicked"))
4. alert('This will be displayed only once.');
5. $(this).addClass("clicked");
});
Listen to anything with the class someLink for a .click()
Stop the browser doing what it would normally do.
Check if the object has the class clicked (Note this could be any name you wanted)
It hasn't so do something.
Add the class clicked so next time its clicked, it will ignore your code.
See the demo here
The problem is that after the listener has been unbound there is nothing stopping the browser from honoring the link (Which it is treating as an anchor tag) and trying to go to it. (Which in this case will simply lead to the top of the page.
I have an ajax app that will run functions on every interaction. I'd like to be able to run my setup function each time so all my setup code for that function remains encapsulated. However, binding elements more than once means that the handler will run more than once, which is obviously undesirable. Is there an elegant way in jQuery to call bind on an element more than once without the handler being called more than once?
User jQuery one function like Tom said, but unbind the handler each time before binding again. It helps to have the event handler assigned to a variable than using an anonymous function.
var handler = function(e) { // stuff };
$('#element').unbind('click', handler).one('click', handler);
//elsewhere
$('#element').unbind('click', handler).one('click', handler);
You can also do .unbind('click') to remove all click handlers attached to an element.
You could attach the event to document with the one() function:
$(document).one('click', function(e) {
// initialization here
});
Once run, this event handler is removed again so that it will not run again. However, if you need the initialization to run before the click event of some other element, we will have to think of something else. Using mousedown instead of click might work then, as the mousedown event is fired before the click event.
You can also use .off() if unbind doesn't do the trick. Make sure the selector and event given to .off exactly match the ones initially provided to .on():
$("div.selector").off("click", "a.another_selector");
$("div.selector").on("click", "a.another_selector", function(e){
This is what worked for me in resolving the same ajax reloading problem.
The answer from Chetan Sastry is what you want. Basically just call a $(element).unbind(event); before every event.
So if you have a function like loadAllButtonClicks() that contains all the
$(element).on("click", function (){});
methods for each button on your page, and you run that every time a button is clicked, this will obviously produce more than one event for each button. To solve this just add
$(element).unbind(event);
before every
$(element).on("click", function (){});
and it will unbind all events to that element, then add the one click event.
In my app a user clicks a link to another page. I'd like to track that in Omniture with a custom event, so I've bound the omniture s.t() event to the click event. How can I make certain the event fires before the next page is requested?
I've considered event.preventDefault() on the click event of the link, but I actually want the original event to occur, just not immediately.
omniture's s.tl() function has a built-in delay
Some thing like this:
var cachedEvent = yourElement.onclick;
yourElement.onclick = function(){
s.t(); // Omniture thingy
cachedEvent(); // Old event
}
I don't know what Omniture events are, but just have
yourElement.onClick = function(){
omnitureFunction();
}
onmitureFunction = function() {
//stuff
myOtherFunction();//what onClick is "supposed to do"
}
So function2 happens only on successful completion of function1
I have added an onclick event, which opens a new tab/page, to a button which has an action already (I don't know which action cause it isn't displayed in source, or maybe it's a form submit action).
Button originally also opens a page in new tab, what I want to do is fire up some function after my onclick attribute executes which will stop execution and that default page opening will not happen, only my page will load.
Is there something that can be done?
It sounds like the event is bubbling up after you have handled it. To stop this happening, add event.cancelBubble = true at the end of your handler.
Using jQuery, you can do this:
$('button').click(function() {
//do something
return false; // stop the event from propagating
});
Try getting your function that your call on the onclick event to return false. This will cause any existing action by the button to be overridden
If the handler was set using 'onclick=' you can simply rewrite the onclick property.
If a handler was added to an element with addEventHandler or attachEvent, you can remove it with removeEventHandler or detachEvent, using the same parameters that were used to set it.
If you don't know the setter, or if it used an anonymous function, you can't remove it.
You could replace the element with a duplicate that has no events and set your own on the new element.
(cloneNode is not supposed to clone events, but it sometimes does in some browsers.)