jQuery delegated event binding not working? - javascript

I am using jQuery 2.2.4. I have three .note_edit elements in my page, added dynamically after page load (they are part of CartoDB infowindows).
I am using the following code in the console to check that the elements exist, which they do:
$("body .note_edit");
Then I am trying to add a dynamic event in the console as follows:
$('body').on('click', '.note_edit', function(e) { e.preventDefault(); console.log('whatever'); });
But when I click on the elements, nothing happens - no console output.
What might be going wrong here? There aren't any other JS errors in the console, the elements definitely exist... what could it be?

Check for other events bound to the elements. Can I find events bound on an element with jQuery?
I suspect there is a e.stopPropagation() preventing body from being reached.

Related

Can't target elements within jQuery datepicker

I am having trouble targeting elements within jQuery ui-datepicker with the plugin WooCommerce Bookings.
Every time I target it using JavaScript it returns null, so the EventListener can't be executed.
However, if I target anything outside the jQuery ui-datepicker I can actually execute the events created with the JS and jQuery.
This is the first time I have encountered something like this and I'm finding it very unusual.
There are the two snippets I have used to test whether or not it can identify the element:
jQuery('.hasDatepicker').on('click', function() {
alert('hi');
});
function showVolunteers() {
alert("hi");
}
document.querySelector(".hasDatepicker").addEventListener('click', showVolunteers);
Here is a JS fiddle with the HTML for the datepicker: https://jsfiddle.net/e5dnru0e/3/
The datepicker is nested within a fieldset, which I can target so I thought maybe I could try use jQuery('fieldset div.ui-datepicker') but that did not work either.
To triple check I was using the correct selector I tried using some CSS and the CSS works perfectly, so there isn't something wrong with my selector.
Is it possible that it has somehow restricted jQuery to be used within this datepicker.
Any help or suggestions will be greatly appreciated.
When your are registering an event for any DOM element then it should be present in DOM at the time of registering.
In case of dynamic controls (which are injected to DOM after DOM ready event) you can use following syntax of jquery for registering an event.
$(document).on('click','.ui-datepicker', function() {
alert('hi');
});
Above code attaching click event on document (which is always present on Document ready). Second parameter of on function is [selector] ie. .ui-datepicker
Instead of document, you can attach click event on any other DOM element which is going to
present while registering an event.
i.e
$('.datepicker-container').on('click','.ui-datepicker', function() {
alert('hi');
});

How to avoid multiple loading of jQuery functions?

I am using following code on my page which I am loading in ajax.
$(document).ready(function() {
$('#button_id').click(function() {
//Do Something
});
});
Now When I click on the button action happens multiple times. I know that its happening because I am loading the ajax page multiple times.
Please help me solve this.
You can use .off() to remove existing listeners:
$(function() {
$('#button_id').off('click').click(function() {
//Do Something
});
});
If I am wrong about your implementation I apologize. Your problem may exist because the binding is created on first page load and then on subsequent ajax loads with new scripts being inserted and creating duplicate bindings. You should prevent any bindings from being generated on ajax loads to prevent duplicate bindings unless you are good with cleanup.
If the button you are clicking on exists in the ajax loaded area then you should use delegation to ensure that the click handlers still work.
For example:
$( "body" ).on( "click", "#button_id", function() {
//do something
});
This will add a binding to the body element, but more specifically to the id #button_id. A click event on the button will propagate and bubble up to the body element (or whatever parent element you choose).
This makes it so that dynamic elements can be inserted in the DOM and only one event handler is needed to listen for it.
No need for .on() or .off() calls for individual ajax loads. This allows your bindings to be much cleaner.
Of course, if your button is not likely to exist on the page all the time then it would not be a good idea to keep extra bindings. Only create these types of binding if they are always needed to prevent optimization issues.
A cleaner solution would be to remove that code from the ajax loaded HTML and use one single event handler in the master page
I guess your problem is the event is firing many times.
To fire only once try this:
$(document).ready(function() {
$('#button_id').on("click",function(e) {
e.preventDefault(); // This prevents the default non-js action (very used for anchors without links or hashes)
e.stopPropagation(); // Prevent the bubling of the event and spread more times
//Do Something
});
});
If doesn't work with e.stopPropagation(); try with e.stopInmediatePropagation();
Adding documentation for the last method I suggested. It could solve your problem.
http://api.jquery.com/event.stopimmediatepropagation/

How do I bind a click event to an object that is not yet in the DOM?

First of all, I tried the answers in this, and this similar questions, but that does not seem to work for me at all.
I would like to do stuff on a click event bound to an element that is created via ajax so it is not in the DOM at first.
I notice that the following coffeescript in my_asset.js.coffee works as expected:
$ ->
$('#div').on "click", ".link", (e) ->
#do stuff
According to JQuery Doc:
this function is bound to all "selected_div" click events, even if they are added to the DOM via ajax later
And the do stuff part works ok
However I would like to:
$(this).after("<%= insert some long view here %>")
So, in order to do that, I guess I should move the $(this).after part from the asset.js.coffee to my_view.js.erb
where I could embed render partial
There, in my_view.js.erb, I have tried the following, (equivalent) javascript:
$(function() {
$("#div").on("click", ".link", function() {
$(this).after("<%= render partial here %>");
});
});
But it does not work for the first click, (it does, however, for the subsequent clicks)
I think it is related to the fact that .link is not in the DOM when the page loads for the first time.
Why is this happening? Is that the cause? And how could I get in the view the same behaviour that I get in the asset ?
You'll bind to a container element (typically document or body), and delegate the event to the to-be-created object:
$(document).on("click", "#div .link", function() {
$(this).after("<%= insert some long view here %>");
});
Binding
As you've pointed out, JS binds events to elements of the DOM on page load
If your element is not present at load, JS won't be able to bind, thus causing a problem. To fix this, you'll have to delegate the bind to an element higher in the DOM hierarchy, allowing JS to bind the event to any new elements in relation to your container

jquery issue with on and live

I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});

jQuery is not catching click on some content loaded

I'm using jQuery 1.7.2 with Zoomy and jmpress plugins. Also I'm using boilerplate+bootstrap downloaded from initializr.com
I'm trying to create a "game" like [Waldo/Wally] when you have to find some character in a photo. Each photo has a different character to find.
I'm using jmpress as a presentation plugin to go from one photo to another every time the character is found. jmpress loads the content trough ajax (and I need that behavior) because I want a pretty fast load of the web.
Problem: The .on("click") event is not being caught on one of the elements that exist inside the content loaded.
As an example, I'll explain my problem with one of this characters (just taking parts of code).
I have in my index.html some divs to load the characters, I'll take the nurse character:
<div id="nurse" class="step container" data-src="women/nurse.html" data-x="7500">
Loading...
</div>
The jmpress load the data-src (women/nurse.html) trough ajax when the user is near to that div (step). It loads great.
This is the code of nurse.html
<script type="text/javascript">
new Image().src = "img/nurse_big.jpg";
</script>
<div class="descripcion">
<p>Bla, bla, bla.</p>
</div>
<div class="imagen">
<img src="img/nurse.jpg" alt="Find the nurse" />
</div>
As you can see, I have two divs loaded inside the #nurse div (that has .step class).
I have this code on my js/script.js file when I try to catch the click event:
$(".step").on("click", function(event){
console.log(event.target);
});
I'm also trying with "body" tag to see what happens
$("body").on("click", function(event){
console.log(event.target);
});
If you check the console while the message is showing (div.descripcion) it catch the event and print. But, after the div.descripcion is removed and the image appears, it dosen't. Like if that div.imagen or even elements inside it dosen't exist. The click event is not catched. I tried to catch mousemove event and It does.
Why is not catching the click? any idea?
You can see a working version: [Removed]
And the not working version: [Removed]
UPDATE: I forgot, if I use .on("click") it dosen't work. But if I use .on("mousemove") for example, it works. That's the weird part. .on() is working, but not for the click event.
UPDATE 2: I have removed the links of the live examples because they where dev versions. I'll publish the link to the final work when is published. Thanks to all of you for taking the time. Specially to #Esailija that gives me the answer.
Once again, you need to use on for content loaded later on:
$("body").on("click", ".step", function(event){
console.log(event.target);
});
Replace body with the closest static element that holds the .step elements.
Static means exist in the DOM when the you execute the line:
$(...).on("click", ".step", function(event){
Example:
$('#ContainerId').on("click", ".step", function(event){
// Do what you want.
});
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers
on docs
The zoomy plugin you are using does this:
'click': function () {
return false;
}
Since the element you are clicking when you are on the image, is actually the zoomy elements, those get to handle the events first. They handle it by returning false, which means doinge.stopPropagation() as well as e.preventDefault(). So the event won't even come to .imagen.
There is also unterminated multi-line comment in your code, not sure what that does but it can't be good. Consider just deleting code instead of commenting it out.
Anyway, clearing everything like this:
$.cache = {}; //Can also do $("*").off() I think
And then doing:
$(".step").on("click", ".imagen", function(event){
console.log(event.target);
event.preventDefault();
});
And it works fine. You might wanna edit the plugin to do this instead:
'click': function (e) {
e.preventDefault();
}
Alternatively you could look for a plugin that is developed by someone who knows what the hell they are doing or write it yourself.
In the documentation in http://zoomy.me/Options.html you can allow the plugin to have a clickable area by adding in true to the clickable option.
So when calling zoomy() on a element all you have to do is add a little bit of code inside the zoomy function.
$('.element').zoomy({clickable:true});
and that should fix everything,
The alternative way to catch the function on click event is just like below.
<div onclick="fireClickEvent();" > Just firing the click event!</div>
function fireClickEvent() {
console.log(event.target);
}

Categories