I have two functions hooked on the submit event of a form. Each function is in a different place, and one function can affect the other.
How can I force one of these functions to be hooked with the lowest priority (ie. be the last to be executed)?
There are four ways I can think of:
Manage the callbacks yourself, and only have one event handler that calls the functions in the desired order.
In the function that is supposed to fired last, do the actual work in a zero-millisecond timeout. If all other functions work synchronously (and you can live with the final one not happening during the event bubbling), this will achieve the same thing.
Bind the to-be-fired-last handler higher up in the DOM tree. You'll have to test if that works with submit events; bubbling of these events doesn't work in IE, but the jQuery docs mention that this has been normalized in jQuery. May be worth a try.
Somewhat similar to 1., when binding any other handler than the low-priority one, unbind the latter, bind the new one, and finally re-bind the last-to-be-run.
You could use custom events for the handlers and have the first handler capture the submit to prevent it, do its stuff, fire the event for the second handler and have the second do the submit
If you want to use / can use custom events, you can use https://github.com/fusion-events/fusion-framework
Related
I'm using smarty streets jquery.liveaddress plugin in an angular single page app. I need to unbind all of the event listeners from the liveaddress instance when I transition to next page. Right now, if I come back to the page, all events are fired twice, then the next time on page, three times.
I've tried everything i can think of. My last solution was to unbind all of the event names from the $(document) because it looks like that is where the plugin is attaching all of the events to. but even that didn't work.
jQuery(document).unbind("AddressChanged");
jQuery(document).unbind("AutocompleteUsed");
jQuery(document).unbind("VerificationInvoked");
jQuery(document).unbind("RequestSubmitted");
jQuery(document).unbind("ResponseReceived");
jQuery(document).unbind("RequestTimedOut");
jQuery(document).unbind("AddressWasValid");
jQuery(document).unbind("AddressWasAmbiguous");
jQuery(document).unbind("AddressWasInvalid");
jQuery(document).unbind("AddressWasMissingSecondary");
jQuery(document).unbind("OriginalInputSelected");
jQuery(document).unbind("UsedSuggestedAddress");
jQuery(document).unbind("InvalidAddressRejected");
jQuery(document).unbind("AddressAccepted");
jQuery(document).unbind("Completed");
Ok for anyone trying to implement SmartyStreets jquery.liveaddress plugin in an angular application.
The trick was to not register anonymous event handlers.
In my case I was instantiating SmartyStreets in a directive, so put all the eventHandlers on scope and call liveAddress.deactivate() upon $destroy. No more duplicate events.
In jQuery, event handler functions are stored in an array. Therefore, unbind() function looks just for the function in the aforementioned array. It further means that you can only unbind() event handlers which are already added with bind().
Check out jQuery documentation on .bind()
Are events in JavaScript always fired even if there are no listeners attached?
Lets say "mousemove", I move the mouse but there are no listeners in the whole app, will the browser still construct a new Event and fire it, or will it optimize and consider the fact that if there are no event listeners, just ignore the data.
I assume that each browser works differently and I'm assuming they use patterns like observer and what not, but is there a spec around that states how it should be?
Feel free to downvote this if you feel this is not correct but from my understanding and according to the DOM Level 2 Events Spec there is a sense that events are always constructed and executed but listeners need to be there, of course, to actually register them.
The reason I say "there is a sense that events are always constructed and executed" is because the Spec mentions that
This method allows the registration of event listeners on the event
target. If an EventListener is added to an EventTarget while it is
processing an event, it will not be triggered by the current actions
but may be triggered during a later stage of event flow, such as the
bubbling phase. If multiple identical EventListeners are registered on
the same EventTarget with the same parameters the duplicate instances
are discarded. They do not cause the EventListener to be called twice
and since they are discarded they do not need to be removed with the
removeEventListener method.
So if event listeners are dynamically added, there needs to be a way for the the page to know to register and listen to them. How each browser handles this is probably different as #JAAulde mentioned above but I do not think browsers would optimize for the fact that an event listener exists or not or at least nothing drastic.
When creating click events, I do my best to bind them only once – generally to a parent shared by all the nodes expected to trigger the event. I'm curious, however, what the best practice is with mouseover events: Does it still make sense to bind an event to a parent when the result would be the constant firing of the event on mouseover? What's the most efficient practice?
In order to provide some closure to this question, I'm going to paraphrase/quote some relevant notes from this answer: 'Should all jquery events be bound to $(document)?', which was referenced above by #Faust:
Event delegation does not always make your code faster. Unless you're binding to dynamic elements or a ton of elements, you should bind event handlers directly to the objects where the event happens as this will generally be more efficient.
More specifically, here are times when event delegation is required or advantageous:
When the objects you are capturing events on are dynamically created/removed and you still want to capture events on them without having to explicitly rebind event handlers every time you create a new one.
When you have lots of objects that all want the exact same event handler (where lots is at least hundreds). In this case, it may be more efficient at setup time to bind one delegated event handler rather than hundreds or more direct event handlers. Note, delegated event handling is always less efficient at run-time than direct event handlers.
When you're trying to capture (at a higher level in your document) events that occur on any element in the document.
When your design is explicitly using event bubbling and stopPropagation() to solve some problem or feature in your page.
Original answer by #jfriend00
So, I know this question is long dead, but I figured I might as well answer with a way to do this.
With dynamic-elements, you can establish a mousemove listener on the parent div/container, and then query within the div for elements with a :hover attribute.
For example:
<div class="list-container">
<ul class="dynamic-list-content">
<!-- actual list elements provided by js -->
</ul>
</div>
Then:
var listContainer = document.querySelector('.list-container');
listContainer.addEventListener('mousemove', function(e) {
var hovered = listContainer.querySelector('li:hover');
// do something with the hovered element here.
});
Note that (as you mentioned) this will fire a lot, but no more than if you added a mousemove event listener to the individual entries. And you could debounce this a bit, using data-attributes, unique ids, etc. From my tests though, it's pretty performant in Chrome.
you can also stop the propagation of events. More info here: http://api.jquery.com/event.stoppropagation/ and here event.preventDefault() vs. return false
Is there a way to temporarily disable an event listener?
In my case, I have a third party library (not jQuery centric) that creates mouseover/mouseout events on an element using addEventListener/attachEvent.
Under certain circumstances another event fires on a different element and I need to disable those event listeners. My solution thus far has been to simply unbind the mouseover/mouseout. This usually works fine because that event generally causes the page to refresh.
However, every now and again an error can occur (think validation error) that results in the page not refreshing, and I need to re-attach the mouseover/mouseout event listeners.
Helpful information
It's probably worth mentioning that because the mouseover/mouseout event listeners are created and attached within a third party library I cannot simply assign the event to a variable and bind/unbind it in that manner (which AFIK is the best way to do this).
Update
I had originally asked
Is there a way in jQuery to get the event listeners already assigned to an object?
I have since found out it is impossible to access events assigned by addEventListener/attachEvent: Access events added with attachEvent() / addEventListener() in JavaScript
jQuery uses data to store events internally, so you can use it to get all of the event handlers for an object:
$("#foo").data("events")
You can then remove a specific handler by using unbind:
$("#foo").unbind('click', $("#foo").data("events").click[42]);
Unfortunately, you can't access them. At best, you can remove event listeners using W3C's removeEventListener (docs) and/or Microsofts detachEvent (docs). Once the listener is removed, however, it's gone for good.
There's one caveat with removeEventListener, in that if the event was registered twice, once indicating to capture, and once indicating not to capture, you must remove it twice; once for each case.
To learn more about capturing and not capturing, see the W3C spec.
If you want to temporarily disable an event handler being run, why not just add escape code to the function?
like so:
$('#button').click(function(){
var clicked_element = $(this);
if(elem.hasClass('event-click-disabled'))
{
// logging code so we know exactly what events are being skipped
console.info(
'The click event on following element was skipped',
clicked_element
);
return;
}
alert('Button clicked');
});
Then if you want to disable an event on a specific element, just call
element.addClass('event-click-disabled');
The event handler is still run, but it will return immediately.
Today is jQuery day. I found this in the documentation:
blur() Returns: jQuery Triggers the blur event of each matched element.
blur(fn) Returns: jQuery Bind a function to the blur event of each matched
element.
In other words, the behavior of the function is totally different depending if it accepts or not an argument.
Is this a design mistake or there's a historical reason for this ?
Keep into account that I know nothing about javascript nor jQuery, and I am trying to get a feeling of it.
That's how jQuery is designed, it's the same for all events. To add a handler to an element you use e.blur(function(){...}), and to trigger the event, you use e.blur(). It kind of makes sense, you just have to get used to it.
Definitely not a design mistake, because it goes for multiple events, such as click, however you should be using .trigger('blur')
It makes sense because .blur() or .click() by nature invokes the event handlers attached to the specific event, and all .blur(fn) does is bind it to .bind('event') where behind the scenes it registers the event handlers.