So I'm running into an issue using a program called XCrud, that is supposed to help with database management. The gist of the issue is that the program removes and reinserts its buttons into the DOM, causing my click functions in JQuery to stop working.
$('a[class="btn btn-warning xcrud-action"]').on('click', function() {
intvId = window.setInterval(cleanup, 200);
});
This button is supposed to reset an interval that helps the user along the workflow of the database, but as aforementioned, the button will only trigger once.
Thanks for the help everyone
Use this syntax instead, to delegate the event handler to all members of the class, present and future:
$('parent').on('click', 'a.btn.btn-warning.xcrud-action', function() {
intvId = window.setInterval(cleanup, 200);
});
Where parent is an element higher up in the DOM tree than the a element you're targeting. (I'm assuming with this selector that all the classes in your example apply to a single a element.) This syntax supersedes the older delegate method.
The point is that you are attaching the handler to an element that isn't going to come and go (no matter how volatile your DOM is, you can always use document if you have to), and applying a filter (in terms of a selector) to the element to only apply the handler to the type of contained element that you want. As the doc states:
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.
Unfortunately, the doc also fails to spell out that the [selector] argument of the on method delegates the handler to members of the classes in the selector. You can find that out by looking at the older delegate doc, and examining the examples that they give to convert from delegate to on.
Here's a little working example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
Test
</title>
<!--jQuery and jQuery-UI files-->
<script src="includes/jquery/jquery-2.2.3.js"></script>
<script src="includes/jquery-ui/external/jquery/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('form').on('click', 'button.test1.test2', function(e, ui){
$('form').append('<button type="button" class="test1 test2">"Test"</button>')
alert('click');
});
});
</script>
</head>
<body>
<form>
<button type="button" class="test1 test2">Test</button>
</form>
</body>
</html>
Any new button will have the same behavior as the original one.
Related
I had a "Sign Out" button on one of the pages of my website, but for some reason, whenever I clicked it, the Jquery function that was supposed to be triggered never was triggered. The button worked on other pages of my website, and I spent a while trying to figure out the issue.
Here's my Jquery code:
$(document).ready(function(){
$("#signOut").on("click", function(){
signout();
});
});
After a bit of debugging, I realized that if I did $("signOut").trigger("click") in the console, the sign out worked perfectly. The css hover and active were working on the button, so I was extremely confused. Then I found this Jquery button click() function is not working and the solution worked for me. I changed my code to
$("document").on('click', '#signOut', function () {}
and it worked... but my button wasn't dynamically created.
Eventually, I realized that I had duplicated the html code. I had my button created before my php and after my php code (that's why I didn't see it). After I got rid of the duplication, my old code worked well, but I'm still confused as to why it created all these problems and why the dynamically created button solution worked.
You are seeing the mentioned behavior as there are some differences between click() and on() depending on at which level the handler is associated.
Explanation
click() handler is associated by jQuery with the DOM element having the matched ID. In case you have duplicate IDs, jQuery gets the first DOM element (since ID is needed to be unique) and associates with the handler, rest elements are skipped. Now, when the second or third element is clicked, surely the event is generated but, propagated to the parent elements directly as there is no handler associated with the clicked DOM element.
click() handler association has no effect in case the DOM element is added dynamically at later point of time. This is not your scenario but just for your information.
on() handler (click event) associated with the parent element of DOM elements having duplicate ID gets the event as described above (the event is propagated to parent elements). It then matches by specified ID irrespective of duplications and executes the handler.
Similarly, click() handler associated with the parent element of DOM elements having duplicate ID gets the event as described above and executes the handler.
The above explanation is true not only for click event but, applicable to other event types as well.
Recommendations
Avoid using duplicate IDs in a page. If you still have some sort of duplications, go for class and associate handler by class. Otherwise, associate the handler to a parent element and match children by ID, class, element name, etc.
If you dynamically create DOM elements, go for handler association with the parent element and match children by ID, class, element name, etc.
Example
In the below example:
When Button 1 is clicked you see logs from 3 places: button specific handler, parent specific handler, and parent general handler
When Button 2 is clicked you see logs from only 2 places: parent specific handler and parent general handler.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Click Events</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
$("#b1").click(function () {
console.log("From specific handler: " + this.innerHTML);
});
$("#p").on("click", "#b1", function () {
console.log("From parent specific(b1) handler: " + this.innerHTML)
});
$("#p").click(function () {
console.log("From parent general handler: " + this.innerHTML)
});
});
</script>
</head>
<body>
<div id="p">
<button id="b1">Button 1</button>
<button id="b1">Button 2</button>
</div>
</body>
I am currently working in a big web project where we have lots of dom elements which need a listener for click/change/... events. The first 500 lines of code of the main javascript file look like this.
$( ".bla" ).each(function(e) {
$(this).on("click", function(){
...
});
});
So basically we add like 100 listeners and for each listener we have to iterate over the complete dom tree. I think this will take up considerable computation power. Is there something like a best practice solution to avoid this?
You can use event delegation:
$(document).on("click", ".bla", function(){
// ...
});
Hopefully you learned from the other answers that there was no need to use each. However, there may not even be a need to select all elements with class .bla as in your example.
Rather, if a div, or some other element, contains all of the elements for which you are interested in handling a click event, you can put the event listener on the container, and then you determine which element got clicked by inspecting the target property of the event. This works due to 'event propagation' -- if not handled directly handled on the element you clicked, the event will propagate up the DOM.
Simple example below with just two buttons. This is straight Javascript which should easily enough be converted to JQuery -- hope it helps.
<html>
<head>
<script>
setTimeout(function() {
document.getElementById('container').addEventListener('click', function(event) {
alert('You clicked ' + event.target.innerHTML);
});
});
</script>
</head>
<body>
<div id="container">
<button id="fooButton" type="button">foo</button>
<button id="barButton" type="button">bar</button>
</div>
</body>
</html>
I have a really strange problem. My example code works [here][1] quite fine, but I have the exactly same code in my aptana studio editor and when I try it in Chrome or the Eclipse browser the events just don't fire. I can't imagine what's the problem, because it's exactly the same code ...
HTML
<!DOCTYPE html>
<html>
<head>
<title>OrderScreen</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="js/script.js" type="text/javascript"></script>
</head>
<body>
Test
</body>
</html>
jQuery
$("a").mouseup(function() {
clearTimeout(pressTimer);
// Clear timeout
return false;
}).mousedown(function() {
// Set timeout
pressTimer = window.setTimeout(function() {
alert("hcbdhaf")
}, 1000);
return false;
}).click(function() {
alert("dfsdg");
});
If your code is really as quoted, the problem is that the elements don't exist as of when you try to hook event handlers to them. jsFiddle's default settings hide this problem from you. (Look on the left, and you'll see that your code isn't run until the load event fires — which is very, very late in the page load process.)
To fix it, either:
Move your script tags to the end of your document, just before or after the closing </body> tag. By the time the browser runs your script, the elements will exist. This is the recommendation of the YUI team and Google's web engineers like it too.
Use jQuery's ready event.
In conjunction with either of those, you might also look at using event delegation instead of directly hooking up events on the elements. Your mouseup and mousedown handlers will get attached to each a element individually. That's a lot of hookups. If there's a container that all of those a elements are in (body or better yet, something nearer), you might instead hook the event on that container (since those events bubble) and then check to see if the event originated in an a element. jQuery supports event delegation, doing most of the hard work for you, via delegate (which I like because it's so explicit) and more recently, one of the half-dozen variations of arguments you pass to on.
With the following code:
<head>
<script type="text/javascript">
function popupMsg(msg) {
alert(msg);
}
</script>
</head>
<body>
example.com
</body>
How can I replace onClick with addEventListener within anchor tag ?
Can it be done without assigning ID to the anchor tag?
Probably something like this:
<a href="http://www.example.com" something.addEventListener('click',popupMsg(SomeMsg),false)>example.com</a>
I don't believe it is supported in this way.
You could perhaps try the onload handler:
Link
The above code is untested.
You can use a querySelector (in modern browsers) to select the element:
var anchor = document.querySelector('a')
(this should select the first link on the page)
then attach your event:
anchor.addEventListener( ... );
none of this is done inline on the element, which is arguably better...
So something like:
<script>
// Function to add event listener to the anchor
function load() {
var anchor = document.querySelector('a')
anchor.addEventListener( ... );
}
document.addEventListener("DOMContentLoaded", load, false);
</script>
Browsers support event attaching at differing "levels" which are the standards set by the W3C over time. Directly attaching an event via "onclick" in the DOM (ie <a href="#" onclick="myFunction()"> would be level 0, or using JavaScript to do the same (ie: document.getElementsByTagName('a')[0].onclick = myFunction;) is level 1.
The restriction of levels 0 and 1 is that only one event handler can be attached to each individual element at any one time for any one type of event. To make event attaching more flexible, level 2 events allow for multiple event handlers for each type of event on each element, and are attached with document.getElementsByTagName('a')[0].addEventListener('click', myFunction, true).
A useful guide for switching between the two is at http://jupiterjs.com/news/a-crash-course-in-how-dom-events-work.
If you don't want to add IDs to elements to attach event handlers, jQuery is a library that makes querying the DOM with CSS Selectors really simple (the code above would become jQuery('a').click(myFunction)) - more information is at http://jquery.com
I have a completely ajax driven website, so lately I've had to research delegation techniques. I've learned that .live and .delegate have been deprecated and the new function to use is .on .
If I'm dynamically loading a webpage into a division on the current page (AJAX) like so:
<html>
<head>
<script src="jquery.js">
</script>
<script>
function ajaxTest()
{
$('#content').load('test.php');
}
</script>
<script type="text/javascript">
$(function(){
$(document).on("click", "#map", function(){
alert("it has been loaded");
});
});
</script>
</head>
<body>
<div id="content">
<button onClick="ajaxTest()" value="Click Me">
This is to be clicked
</button>
</div>
</body>
</html>
where test.php looks like
<html>
<head>
</head>
<body>
<div id="map">THIS IS THE MAP</div>
</body>
</html>
I can then click on the words "THIS IS THE MAP" and it does indeed show the alert. The problem I've been having is instead of doing:
$(document).on("**click**", "#map", function(){
I need something more along the lines of:
$(document).on("**load**", "#map", function(){
It doesn't work obviously, so I'm wondering if something similar might. The whole reason I'm even inquiring about this is because in some pages, instead of having just "THIS IS THE MAP" in the map division, I have a google map or an swf object or something. Any help would be appreciated.
If you wanted to just answer how to load a google map into a division that doesn't exist yet, that would be helpful too ;)
Only some events bubble up the parent chain and can be used on parent objects with .on() or .live() or .delegate(). .load() is not one of those events that bubbles.
This is a partial list of events that bubble from the jQuery doc: click, dblclick, keydown, keypress, keyup, mousedown, mousemove, mouseout, mouseover, and mouseup.
From the doc page for .on(), here's a quote:
In all browsers, the load event does not bubble.
In your particular example, you can bind the event directly to the object like this:
$("#map").on("load", function(){});
or
$("#map").load(function(){});
When you bind directly to the object like this, no bubbling is needed and it will work with the load event. The object #map will need to exist at the time you bind the event handler as this method of using .on() or .load() can't work with objects that will be created in the future. The event handler must be attached to the object after the object is created.
if you want to do something when the response is loaded you can make use callback function in load instead of doing it in two separate script and event binding, something like
$('#content').load('test.php', function(){
alert("map is loaoded.")
});
reference