I have multiple images on my page. To detect broken images , I used this found on SO.
$('.imgRot').one('error',function(){
$(this).attr('src','broken.png');
});
This works fine on the first image which I understand. But when I change this to
$('.imgRot').on('error',function(){
$(this).attr('src','broken.png');
});
it does not work on any of the images . Could someone tell me why ?
Community wiki: This generic answer does not contribute to the question OP posted but relative to the title.
The concept of one() and on() can be explained with the below code.
one() function is automatically moved to off state after first instance of occurance.
on() is identical to one() but it needs to be manually put to off state otherwise the number of instances has no limit.
var i = 1;
$('.one').one('click', function() {
$(this).text('I am clickable only once: ' + i);
i++;
});
var j = 1;
$('.multiple').on('click', function() {
$(this).text('I was clicked ' + j + ' times');
j++;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="one">Click me</div>
<div class="multiple">Click me</div>
If you look at the source code for .one() in jQuery 1.7, it just calls .on() internally except that after the event fires, it removes the event handler. So, there should be no difference in your case because error is an event that should only happen once per object anyway.
So, there must be something else going on in your code like maybe the image objects haven't been loaded into the DOM yet when you run this code or something like that.
If you were trying to use delegated event handling to do this (which your example does not show), then you may run into issues where the 'error' event doesn't propagate.
It may also be that your code has timing issues due to caching. Trying to install these types of error handlers on images that are already in the DOM is a race condition. You're trying to get the error handler installed before it gets called, but the image has already started loading and the event might have already fired before you get the event handler installed. Subsequent page loads (after the first) may have cached other page elements or DNS references so it may get to the error handler quicker and perhaps even before your JS can run and install the error handlers.
I know this is an issue with browser caching and the onload event. You can only reliably get the onload event if you attach the event handler either in the embedded HTML (so it's there when the <img> tag is first parsed or if you attach it before the .src property has been set (if creating the image programmatically). That would suggest that you can't reliably set error handlers the way you are doing for images that are in the page HTML.
My suggestion would be this:
Don't try to install error handlers like this after the images are in the DOM.
If you assign them on programmatically generating images, then assign the event handlers before .src is assigned.
If you need these on images in the page's HTML, then you will have to put the event handlers in the HTML with something like <img src="xxx" onerror="yourErrorFunc(this)"> because that's the only way to guarantee that the handlers are installed before the event can occur.
jQuery will reuse the "on" method for the "one". Following is the internal code of jQuery where they'll be passing hardcoded value "1" to the function of jQuery.on() They'll turn off the triggered event further on the element using jQuery.off()
on:function( types, selector, data, fn, one ) {
if ( one === 1 ) {
origFn = fn;
fn = function( event ) {
jQuery().off( event );
return origFn.apply( this, arguments );
};
}
off:function(types, selector, data, fn){
on(types, selector, data, fn, 1);
}
So, in your case "error" is the event type triggered on the first image and when the jQuery.one() method called this event got turned off and then its not triggered for further on the $('.imgRot') elements
Related
Is there a way to get all elements that have a certain event listener attached to them?
I know I can get them if the event listener is defined as an attribute:
var allElemsInBodyWithOnclickAttr = $("body").find("*[onclick]");
But I have elements that have event listeners that are attached by code and thus have no onclick attribute.
So when I do (for example) this:
$("a").on("click", function(evt) {
alert("Hello");
});
..then using the code below doesn't fire the click on those anchor elements:
$("a[onclick]").trigger("click");
I guess I could loop through all the elements and check if they have the listener I'm looking for (using this SO question) but I can't imagine that's going to perform very well..
Can't add comments, but still - consider using https://github.com/ftlabs/fastclick for removing the delay.
That helped me, when i was developing app using cordova.
Also, didn't notice you have already mentioned this post, so i have written implementation for 'loop through all elements'-type-of-solution
Array.prototype.reduce.call(
$('body').children(),
(answer, node) => {
if (typeof $._data($(node)[0], 'events') != 'undefined') {
answer.push(node);
}
return answer;
},
[]
);
I think this is not possible, since it is not possible to test if a single element has an event listener attached to it.
Look here
So the only way to do that is to manage a map which contains a reference to each event handler for each event for each element.
Edit
With respect to the answer of #Ivan Shmidt, I must correct my answer: Obviously It seams to be possible with jQuery. That is because jQuery is holding a reference of attached event handlers, BUT events attached using good old .addEventListener() would bypass this and also not be found this way.
To improve maintenability I'm trying to replace HTML that's being dynamically generated in script with AJAX query results. But there's rather uncommon problem to bind event handlers to dynamic content, although there's a lot of solution much of them work just partially. I mean that if I write
$("#m" + event.PeerId).live("hover", function (args) {
debugger;
}
it works but
$("#m" + event.PeerId + " sendMessage").live("click", function (args) {
debugger;
}
doesn't work, the event doesn't occurs if I click. Also I can trace hover event of the root downloaded element but I can't trace load, ready, etc. - they also never occur.
Tell me please how can I handle button click that is some subelement of the loaded content.
If you are using any recent version of jQuery you should use a delegated event handler:
$(document).on("click", "#m" + event.PeerId + " sendMessage", function (args) {
debugger;
});
If your ids all start with m, but the peerid is not know yet, use this:
$(document).on("click", "[id^=m] sendMessage", function (args) {
debugger;
});
Delegated event handlers should be attached to a non-changing ancestor (document being the default if nothing closer to the changing elements is handy).
They work by listening for the event bubbling up the ancestors, to the one the handler is attached to, then applies the jQuery filter, the calls the function for each matching element that caused the event.
Note: Never use 'body' for delegated event handlers as it has bugs relating to styling (which can cause events to not occur). Always use document if you do not have a closer element that does not change.
I want to make it so that when a track completes playing it automatically calls a javascript function which clicks the next track button so that the playlist continues to play. So far i have:
function next() {
$("#nextTrack").click();
}
But that doesn't seem to do anything when it is called, anyone know where i am going wrong?
The way to trigger the click event on the DOM element itself is with:
$("#nextTrack")[0].click();
This gets the first (only) DOM element matched (removes it from the jQuery wrapper) and calls the native click method.
The only reasoning I can provide for this is because trigger doesn't (or hasn't, in past versions) attempted to call the native method. I swear in the most recent version, I can use .trigger("click") (same as .click()) and it will effectively click the element...executing native any click handlers and jQuery handlers as well.
trigger is tricky because jQuery stores any event handlers bound with jQuery.on, jQuery.bind, etc. in a special place. When the native event is fired, or .trigger("event_name") is used, all those special handlers are executed. But for some reason, the native event isn't always triggered (as you seem to have found out). I'm not sure if it's because of the version of jQuery or the event type.
UPDATE:
The reason this is happening is because jQuery treats <a> specially when using .trigger("event_name"), specifically for the click event.
After blabbering with the stuff above, I decided to look into the jQuery source code for trigger. In the end, the native DOM method is called, with elem[ type ]();. But it only gets to this point under certain conditions...these are the conditions (among other nested if statements):
if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
!(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
And I'm guessing that specifically, the part !(type === "click" && jQuery.nodeName( elem, "a" )) is what prevents it in your case, because the element you're targeting is <a>, and it's the click event.
DEMO: Here's basically just something mimicking your code in a simpler fashion: http://jsfiddle.net/EjVMY/ - notice how the initial console.log on load doesn't execute (because of the trigger). But as soon as you actually click on the event, the console.log does execute. I found that replacing the <a> with a <div> changes the behavior - it logs on load.
Of course, if I had just Googled it in the first place, I would've found: trigger a click on a anchor link
Would it be possible to somehow intercept the DOM ready event so that anything normally triggered within, say, $(document).ready(function() { ... } would not be executed? And then later manually trigger the event so that the code was executed?
In my case, I have a large amount of existing code that already relies on $(document).ready(function() { ... } and my hope is that I would not have to do a search/replace to have all of it rely on a custom manually triggered event.
jQuery.trigger() is made for such need
Any event handlers attached with
.bind() or one of its shortcut methods
are triggered when the corresponding
event occurs. They can be fired
manually, however, with the .trigger()
method. A call to .trigger() executes
the handlers in the same order they
would be if the event were triggered
naturally by the user:
If you do not want something to be "normally" triggered, it should not be in $(document).ready(on_ready_func). Everything inside it will load as soon as the DOM is loaded and before the page contents are loaded.
Instead, from on_ready_func cherrypick the things you want to trigger manually into another function say manual_func, and call that function at event of your choice.
Note that, ready function cannot be triggered using trigger, so you cannot fire the func again, unless you have it as a separate function.
Split-up is your best option.
var manual_func = function(){
// Manual loads
}
var on_ready_func = function(){
// only things to be loaded on dom ready
// e.g.
$('button#load_manual_trigger').bind('click', manual_func);
}
$(document).ready(on_ready_func);
Happy Coding.
I have a site that uses AJAX to navigate. I have two pages that I use a click and drag feature using:
$(".myDragArea").mousedown(function(){
do stuff...
mouseDrag = true; // mouseDrag is global.
});
$("body").mousemove(function(){
if (mouseDrag) {
do stuff...
}
});
$("body").mouseup(function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
});
I just type that out, so excuse any incidental syntax errors. Two parts of the site use almost identical code, with the only difference being what is inside the $("body").mouseup() function. However, if I access the first part, then navigate to the second part, the code that runs on mouseup doesn't change. I have stepped through the code with Firebug, and no errors or thrown when $("body").mouseup() is run when the second part loads.
So, why doesn't the event handler change when I run $("body").mouseup() the second time?
Using $("body").mouseup( ... ) will add an event handler for the body that is triggered at mouseup.
If you want to add another event handler that would conflict with current event handler(s) then you must first remove the current conflicting event handler(s).
You have 4 options to do this with .unbind(). I'll list them from the least precise to the most precise options:
Nuclear option - Remove all event handlers from the body
$("body").unbind();
This is pretty crude. Let's try to improve.
The elephant gun - Remove all mouseup event handlers from the body
$("body").unbind('mouseup');
This is a little better, but we can still be more precise.
The surgeon's scalpel - Remove one specific event handler from the body
$("body").unbind('mouseup', myMouseUpV1);
Of course for this version you must set a variable to your event handler. In your case this would look something like:
myMouseUpV1 = function(){
if (mouseDrag) {
do stuff...
mouseDrag = false;
}
}
$("body").mouseup(myMouseUpV1);
$("body").unbind('mouseup', myMouseUpV1);
$("body").mouseup(myMouseUpV2); // where you've defined V2 somewhere
Scalpel with anesthesia (ok, the analogy's wearing thin) - You can create namespaces for the event handlers you bind and unbind. You can use this technique to bind and unbind either anonymous functions or references to functions. For namespaces, you have to use the .bind() method directly instead of one of the shortcuts ( like .mouseover() ).
To create a namespace:
$("body").bind('mouseup.mySpace', function() { ... });
or
$("body").bind('mouseup.mySpace', myHandler);
Then to unbind either of the previous examples, you would use:
$("body").unbind('mouseup.mySpace');
You can unbind multiple namespaced handlers at once by chaining them:
$("body").unbind('mouseup.mySpace1.mySpace2.yourSpace');
Finally, you can unbind all event handlers in a namespace irrespective of the event type!
$("body").unbind('.mySpace')
You cannot do this with a simple reference to a handler. $("body").unbind(myHandler) will not work, since with a simple reference to a handler you must specify the event type ( $("body").unbind('mouseup', myHandler) )!
PS: You can also unbind an event from within itself using .unbind(event). This could be useful if you want to trigger an event handler only a limited number of times.
var timesClicked = 0;
$('input').bind('click', function(event) {
alert('Moar Cheezburgerz!');
timesClicked++;
if (timesClicked >= 2) {
$('input').unbind(event);
$('input').val("NO MOAR!");
}
});
Calling $("body").mouseup(function) will add an event handler.
You need to remove the existing handler by writing $("body").unbind('mouseup');.
jQUery doesn't "replace" event handlers when you wire up handlers.
If you're using Ajax to navigate, and not refreshing the overall DOM (i.e. not creating an entirely new body element on each request), then executing a new line like:
$("body").mouseup(function(){
is just going to add an additional handler. Your first handler will still exist.
You'll need to specifically remove any handlers by calling
$("body").unbind("mouseUp");