Changed data attribute not recognized in jquery selector - javascript

I've the following html structure
<body data-page="first">
<div class="start">Test</div>
</body>
and the following js
$('body[data-page="first"] .start').on('click',function (){
body.attr('data-page','second');
});
$('body[data-page="second"] .start').on('click',function (){
console.log('Test');
});
I would expect, that after the second click on .start, the console would show "Test", but it doesn't...
Can you tell me what I'm doing wrong?
Thanks in advance!

While you have your answer, I don't think the essential point has been made in any of the answers so far, and that is that the binding of an event handler must happen after the target element exists.
When you try to bind an event handler to a particular element in the DOM, the element must exist at the time. If it does not exist, the handler has nothing to bind to, and so the binding fails. If you later create the element, it's too late, unless you re-run the binding statement.
It will soon become second nature to call appropriate event handler binding statements after you create a new element (by modifying the HTML using javascript) that needs a handler.
For instance, in my current project I regularly make AJAX calls to a server to replace blocks of HTML as things happen on the page. Even if some of the new elements are exactly the same as the ones being replaced, they will not inherit any bindings from the replaced elements. Whenever I update the HTML I call a function that contains necessary statements to bind my event handlers to the new copy of the active elements.
Your code would work if you made the following change:
$('body[data-page="first"] .start').on('click',function ()
{
body.attr('data-page','second');
$('body[data-page="second"] .start').on('click',function (){
console.log('Test');
});
})
A couple of other (off-topic, but related) points:
It's possible to bind a handler to an element multiple times. The trick to avoiding this is to include the .off() method in the chain before binding (noting though that .off("click") will unbind all click handlers bound to that element, not just yours) e.g.
$("#mybutton").off("click").click(function(){myHandler()});
"the arrow function doesn’t have its own 'this' value" () so don't use arrow functions in event handlers if you plan to reference any of the element's properties via 'this'. e.g.
$("#mybutton").off("click").click(() => {console.log(${this.id})}); // >> "undefined"

The issue is that the page is rendered with the data-page set to first, and when you click again on it, that part of javascript still see "first", since is not rerendered, so you need a dynamic function, the read all the intereaction with that button, and than check wich value that attribute has. Like this you can make infinite cases, and still go on.
$('body .start').on('click',function (){
const attr = $('body').attr('data-page');
if(attr === 'first') {
$('body').attr('data-page','second');
} else {
console.log('second');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body data-page="first">
<div class="start">Test</div>
</body>
And if you don't like the fact that is targetting all the "body" wich is weird, becouse you should have only 1 body, you can use an ID to target the right one
PS: is never a good idea to duplicate your function, if you can set everything in a dynamic function, that reads everything, is easier to debug in the feature, and is lighter and more clean to work on

$('body[data-page="first"] .start').click(function (){
var body = $('body[data-page="first"] .start');
body.attr('data-page','second');
});

This method can help :
var timesClicked = 0;
$('.start').on('click',function (){
timesClicked++;
if (timesClicked>1) {
console.log('Test');
}
});

Related

when you create an object which has events, but isnt added to the page, it doesnt save handlers

I have a fiddle which creates a viewer for a set of data. If you are looking at the javascript, it will look at 3 lines, if you search for SEARCH_HERE
$("body").append("TEMPLATE<hr />Maintaining Object").append($maintence);
//$("body").html($maintence);
//$("body").html($_table);
The fiddle is located at: http://jsfiddle.net/fallenreaper/wFGW6/1/
The first one will show the TEMPLATE on the page and then adding new ITEMS will all have working events when doing
var $data = $_table.clone(true,true);
in the addBlock() function.
If you only uncomment the second line, it will JUST show the maintainer item.
When you add items [+], you will show the form, but the events would not be there.
I was thinking that since $_table is removed from the page, the events are not there any longer. The 3rd line, pretty much reappends $_table to the document, and the events are not there.
IS this suppose to be like this? Should i instead just create a wrapper function which is executed inside of addBlock() to attach all the handlers accordingly?
This is rather odd.
EDIT:
One answer, pointed to delegated events, which seems like it could work. There is an issue though that seems to set $(this) to a new object, the body tag, instead of the selected element.
inside of a click event would be redefined as:
$("body").on("click", $expander, function(){...});
//instead of:
//$expander.click(function(){...});
I was thinking to just do something like left-hand assignment, something like:
$(this) = $expander;
but according to a website, left-hand assignment doesnt work. (http://hungred.com/how-to/tutorial-override-this-object-javascript/). They did point me in a direction which would be VERY useful.
function example(eventHandler){
ALL MY CODE.
}
$("body").on("click", $expander, function(event){
example.call($expander, event);
});
Does this look feasible, or should i be planning another route?
You can use jQuery's .on() to do some event delegation. Your code is too long for me to read and edit, but in a nutshell rather than having $add.click(function() { ... }) you bind the event listener to the parent or body $("body").on("click", ".addNew", function() { ... })

Why is jquery event delegation not working?

I have the following html:
<ul id="contain">
<li class="active_one"></li>
<li class="two"></li>
</ul>
And the following jquery:
$contain = $('#contain'); //going to use a lot
$contain.on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
The above does not work, but When I change it to:
$('body').on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
Then it works, but I know the best practice is to drill as close as possible to the parent element of what I am trying to work with, yet every time I try to do that, I am clearly doing something wrong because my parent level selectors are not working.
It means that #contain itself is not a static element, you should select closest static parent of the element. Otherwise jQuery doesn't select the element and delegation fails.
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on().
However, in case that element is static, you are selecting the element too soon, you should wait for DOM to be ready.
$(document).ready(function(){
var $contain = $('#contain'); //going to use a lot
$contain.on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
})
So, I will try to explain how jquery on(change) works,
if you have
$(document).on('parameter 1','parameter 2', function(){} )
essentially, jquery will check if event mentioned in parameter 1 is executed or not. Then it will check if parameter 2 is present,
if so, Then it will check if the event mentioned in parameter 1 was triggered in the element mentioned in parameter 2. hence you can prevent on(change) events whenever the document changes.
this will help you to bind dynamically loaded elements by making sure the loaded elem has id and then binding events to that id alone. and whenever a document event occurs, the dynamic one will be executed.

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

Use from `$.each` in several times separate.?

After search and click on result search and click on plus(button add input) next to input "INSERT VALUE HERE" in the example, in new input $('.auto_complete').keyup(function () { ... not work.
I believe that have to bind the events separately and use a closure so that each element has its own set of variables(or change the logic so that only use the value in the field and don't need any state variables),
how is it?
EXAMPLE: see here
Js full code: http://jsfiddle.net/6yPxn/
$.each:
var ac = $(this).text();
var ok = $.grep(data, function (e) {
return e.name == ac;
})[0].units;
$.each(ok, function (bu, key) {
//alert(key.name_units);
$("<div class='mediumCell'/>").hide().fadeIn('slow').append('<b>' + key.name_units + '</b>', $('<div class="column" style="float: left;" />')).appendTo(".list_units");
});
It runs fine, but I don't see anywhere in that code you've provided where you're adding an event handler to the input box.
The issue is in http://www.binboy.gigfa.com/files/js/admin.js, somewhere around the top:
$('.auto_complete').bind('keyup',function () {
/* ... */
});
When the page loads it binds several event handlers to input boxes and the like. When you create a new one this functionality is not added unless you're using jQuery's .live or something similar. As the documentation notes:
This method [.live()] is a variation on the basic .bind() method for attaching event handlers to elements. When .bind() is called, the elements that the jQuery object refers to get the handler attached; elements that get introduced later do not, so they would require another .bind() call.
I don't really want to wade through all the nested click and delegate and bind calls, but I guarantee you that's where your problem lies. To fix it you'll probably need to have either the autocomplete section run on your newly created node, use .live instead, or just .clone the original.

Setting onclick event removing the handlers attached before

What is the difference betwen setting the onclick function in this way:
obj.onclick=new Function('functionname')
and
obj.onclick=function(){ functionname();};
How can i set the onclick event removing all previrius attached? (using jquery or simply javascript)
i try something like this:
$(obj).unbind('click');
$(obj).click(function() {
functionname();
});
but the unbind seems to remove even the next attached events.
thanks
EDIT:
I set the click event using jquery:
function ON(){
$('#makecorr').unbind('click');//i unbind for some reason..
$('#makecorr').click(function() { OFF(); });
}
function OFF(){
$('#makecorr').unbind('click');//i want to remove prev attached fun
//and replace it with new event
$('#makecorr').click(function() { ON(); });
}
this doesnt work for me, when i click the object 'makecorr' it goes in loop , if i put an alert, it comes up infinitely
but when i use: '
function ON(){
$('#makecorr').get(0).onclick=new Function('OFF()');
}
function OFF()
{
$('#makecorr').get(0).onclick=new Function('ON()');
}
it works. strange where am i wrong?
This pair of lines:
$(obj).unbind('click');
$(obj).click(function() { functionname(); });
...if executed in the order above should result in just the handler you've defined there being attached. If you're seeing other behavior, it must be something else in the script.
Regarding
What is the difference betwen setting the onclick function in this way:
obj.onclick=new Function('functionname') and
obj.onclick=function(){ functionname();};
If you're dynamically adding handlers to an element and you're already using jQuery for other reasons, you don't want to use the onclick property at all; stick to the jQuery API, to avoid really confusing yourself. :-)
Even if you're not using jQuery, you almost never want to use new Function(...). There are only very special situations where you'd need that. Mostly stick to function() { functionname(); } (or just use functionname directly).

Categories