Does jQuery handle events without setting the normal event handlers? - javascript

With a div on an HTML page, I used jQuery to add a click event handler:
$(document).ready(function(){
$("div").click(function() { alert("clicked!"); });
});
Then I used the Visual Studio 2008 (or IE8) debugger to step through the javascript/jquery. I expected to see a value in the debugger for:
$(“div”)[0].onclick
($(“div”)[0] being the first div in the collection of results of the selector, $(“div”)).
But I don’t. I thought jQuery’s event assignment methods (e.g., .click(function)) were actually assigning values to an element’s underlying events. They’re not?
In other words, these two lines of code don’t have the same affect, but I thought they would:
$("div").click(function() { alert("clicked!"); }); // (Line 1)
$("div")[0].onclick = function() { alert("clicked!"); }; // (Line 2)
Can anyone explain this or point out if I’m doing something wrong? I would like to use line 1 in my code, but for my needs it seems I’ll have to use line 2 (FYI, I am planning to use an actual function, and not just show alerts :-) ).
Thank you

jQuery does not use the onclick attribute to attach events to elements. It uses either attachEvent(IE) or addEventListener(all others). You can find the smoking gun in lines 2500-2502 of the uncompressed jquery-1.3.2.js.
You should be able to, however, place a breakpoint inside your jQuery event handler and have Visual Studio stop there. Either click inside the body of the event handler (e.g. on "alert") before hitting F9, or break the event handler body into a its own line, e.g.
$(document).ready(function(){
$("div").click(function() {
alert("clicked!"); // place a breakpoint on this line
});
});

Related

Changed data attribute not recognized in jquery selector

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

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() { ... })

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

how to prevent mutiple registration of a single event handler in jquery

I have a <div> box displaying search message and some radio button for recent message. There is link option for slide toggle.
When you click on that link it will show some input field and check box and radio button. And at the same time the text of link change to hide option. If you click on that it will hide all the input and checkbox option.
When I refreash the whole page its working properly but when that paticular box or part is refreashing then the box is hiding and imediately hides. If you refresh that portion n number of times the box is going on toggling continously. I think the problem is in registration of event handler. So please give me some solution.
CODE :
$(document).ready(function() {
$(".SideBar-blockheader1").click(function() {
e.preventDefault();
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".SideBar-optionheader").click(function() {
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").attr('value') ? $("#showopt").attr('value') : $("#hideopt").attr('value'));
});
$(".SideBar-optionheader").text($("#showopt").attr('value'));
$(".SideBar-optioncontent").hide();
});
jQuery has a method, called data() which can be used to extract the attached handler information of an HTML element. You can see if the element has already a click handler, and if it has, then stop re-attaching another handler to it.
if(typeof $('#id').data('events').click == 'object')
{
// A click handler is already attached
}
else
{
// No click handler; Attach one;
}
Although you haven't provided code, I suspect you are using .click(). For jQuery 1.7+ you should be using .on() in delegate mode (the element you bind to is an ancestor, not the clickable element itself) or .delegate() if pre jQ 1.7.
For example:
$('someAncestor').on('click', 'a.specialLink', function(event) {
event.preventDefault();
// the rest of your code for the click handler
})
"someAncestor" is any valid selector that is an ancestor of your link that will not be destroyed, rebuilt, or otherwise manipulated after the DOM is built. It doesn't have to be the direct ancestor.
[updated below after seeing code sample and comments]
There are a few things going on. First, .on() will only work if you're using jQuery 1.7+. Next, .on() can be invoked a few different ways (I wrote about it here: http://gregpettit.ca/2011/jquery-events-its-on/) and you need to be invoking it while delegating an ancestor listener, not simply as a substitute for click. Next, you haven't provided code for your attempted update, only for the original code; it's hard to tell what "didn't work" about trying to use .on(). Moving along, I'm not actually sure what this line is meant to do:
$(this).text($(this).text() == $("#hideopt")...etc...
I can't think of why you would want to try to treat a jQuery object as a variable. I'm not saying the code is wrong, I'm just saying I don't get it. Also, I hate ternary operators... which is part of the reason I don't get it. I much prefer readable conditionals. ;-)
Next, you're calling preventDefault() on "e" but you're not passing "e" into your functions. You might just be getting a JavaScript error, period. (e is undefined)
Then there's attr("value") which I believe should actually work. But why not use .val() if it is indeed a node that HAS a value attribute?
Finally, there is tonnes of room for caching your objects. Every time you see that an object is being used more than once, you can benefit (to varying degrees of performance and legibility) from caching it. I have not updated the code with any caching, though-- that's really something for a whole other "How can I best cache my objects?" question.
Anyhow... to solve the problem, you first have to choose a valid ancestor. This can be any ancestor that isn't destroyed during the process of loading in your new data. This could be anything, but the closest ancestor is the best. It might be a section wrapper, but if you're truly desperate it could be a page wrapper or even the body tag. If you bind to document, you're reproducing the deprecated .live() function, which I definitely recommend against. I have used a placeholder selector, ".section" but you need to figure out what an appropriate ancestor is on your page.
$(document).ready(function()
{
$(".section").on("click", ".SideBar-blockheader1", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".section").on("click", ".SideBar-optionheader", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").val() ?$("#showopt").val() : $("#hideopt").val());
});
$(".SideBar-optionheader").text($("#showopt").val());
$(".SideBar-optioncontent").hide();
});

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