Consider the HTML
<ul>
<li>Default item</li>
<li>Default item</li>
</ul>
<button>Append</button>
and the jQuery code
$('button').live('click', function(){ //This action is done by an external script.
$('ul').append('<li>Added item</li>');
});
$('ul li').append('<b> x</b>'); // This action is done by me
The thing is, I need to append the "x" mark to all newly added elements to the dom.
In this case only default elements are appended with the "x" mark.
Newly added elements are not appended by "x".
I am sure the work will be simple, but cant get it right !!
Live example - http://jsfiddle.net/vaakash/LxJ6f/1/
Your code is running right away, and so of course it only has access to the elements that already exist. The code adding new list items is running later, when the user clicks something. You'll have to hook into that process as well.
One way is to hook the same event they are, and run your code from the event handler. Be sure to hook the event after they do.
Their code:
$('button').live('click', function(){
$('ul').append('<li>Added item</li>');
});
Your code (after theirs):
$('button').live('click', markButtons);
markButtons();
function markButtons() {
$('ul li:not(.marked)')
.addClass("marked")
.append('<b> x</b>');
}
Updated fiddle
The reason I said your code needs to do its hookup after their code is that jQuery guarantees the order of the calls to event handlers: When the event occurs, the handlers are called in the order in which they were attached. By attaching your handler after they attach theirs, you guarantee that your handler is called after theirs has done its thing.
If you're worried about the order getting mixed up, you could always delay your code slightly:
$('button').live('click', function() {
setTimeout(markButtons, 0);
});
That way, your code is guaranteed to run after all of the event handlers hooked to the click have been run.
You have to repeat the "x" code in the event handler:
$('button').live('click', function(){ //This action is done by an external script.
$('ul').append(
$('<li>Added item</li>').append('<b>x</b>')
);
});
Of course you could also just put the bolded "x" right in the <li> when you append it ...
edit If you can't change the click handler, then the only thing you can do is either poll the DOM, or else try something like what #T.J. Crowder suggests (which I think should work just fine).
Why not just do it in the initial append?
$('button').live('click', function(){ //This action is done by an external script.
$('ul').append('<li>Added item<b> x</b></li>');
});
Since it sounds like you don't have access to the script that is doing the append, you could bind your own handler.
$('button').live('click', function(){ //This action is done by an external script.
setTimeout(function() {
$('ul li:not(li:has(b))').append('<b> x</b>');
}, 10);
});
This will select li elements that do not currently have a nested b element, and it will append one.
I placed it in a setTimeout since you may not be able to guarantee the order of execution of the handlers.
If you prefer valid CSS selectors, do this instead:
$('ul li').not($('li b').closest('li')).append('<b> x</b>');
or this:
$('ul li').not(function() { return $(this).find('b').length; }).append('<b> x</b>');
JSFIDDLE DEMO showing the two separate handlers.
Related
I'm sorry, if my question is very simple, but can't understand it. As I understand it's good practice to attach links/buttons listeners via js but not html. I mean this is bad practice:
<a href="javascript:" onclick="temp();">
And this is good
<a href="javascript:" id="mylink">
in js code
$("#mylink").on...
As I understand the main idea is that html page mustn't contain any javascript code, and all js code must be in js file. Ok, I like this idea.
The problem I see, implementing such approach is when to attach such listeners.
All I found - is that listeners can be attach only when document is ready (otherwise we can't add listeners to elements that are not ready).
However, if I have a page that shows for example 100 rows from database, then its loading is not fast (at comparison to short pages).
And if we wait until document is ready, the user can't work with site.
Please correct me if I'm wrong or say how can we make it to work with long pages without such things as timeouts.
You can use delegated events: Instead of waiting for an element to be created/ready and then attaching an event handler, the handler is attached to a parent element, that exists early, such as the document object, since that always exists.
Example: Instead of
$('#mylink').on('click', function() { /* do something */ });
it's
$(document).on('click', '#mylink', function() { /* do something */ });
This is normally used when elements are added dynamically, but it works just as well "while the page loads".
EDIT: Short explanation:
Basically DOM events "bubble": When an element is clicked, then the event isn't just sent to that element, but to all of its ancestors, one by one going up the DOM tree. The event contains information which element was originally clicked and you could filter/check for the element yourself:
$(document).on('click', function(event) {
if( $(event.target).is('#mylink') ) {
alert('the link was clicked');
}
});
Using the selector as the second argument in .on() jQuery does the filtering for you, and sets this to the link instead of the document.
See also this example: http://jsfiddle.net/qhsktpcs/
Make sure to add all your JS code in this block :
$( function( ) {
$( '#mylink' ).on( 'click' , function ( e ) {
console.log ( 'link clicked' );
e.preventDefault();
});
// the rest of your code ...
});
Otherwise, it will try to register click events on elements that are not yet created (since the DOM is not ready yet) and it will wait untill everything's ready ( even if you have ~100 rows to be pulled from the DB like you said )
** EDIT **
Example here : http://jsfiddle.net/6ctd2hqs/1/
You can test if your page is ready, and then start attaching listeners. E.g. for jQuery:
$.ready(function() {
// listeners here
})
I use the following jquery in my page.
var j = jQuery.noConflict();
j(document).ready(function(){
console.log(j("#label_19"));
j("#label_19").on("click",function(){
alert("Clicked");
});
});
When document loads, the element (it's a checkbox) appears in the console. But when I click the checkbox, the alert is not thrown. But when I copy the same code (as below)
j("#label_19").on("click",function(){
alert("Clicked");
});
in console panel and press run. Now when I click the checkbox, the alert is thrown. What could be the issue in this case?
Updated:
What I observe in console is:
Object[input#label_19.document_label attribute value = "Check-In"]
The HTML markup is
<input id="label_19" class="document_label" type="checkbox" value="Check-In" name="label[19]">
The only explanation that fits the facts you've presented is that there is code running after your ready callback but before you click the element that replaces the element in question. Some kind of ajax callback or similar.
You'll need to look through your code to find the place where that's happening. Things to look for are any html calls on elements that contain the #label_19 element, or (if there's a mix of jQuery and non-jQuery code) assignments to innerHTML.
You can use event delegation to solve this, which may or may not be the best answer depending on what your code is doing. That looks like this:
var j = jQuery.noConflict();
j(document).ready(function(){
console.log(j("#label_19"));
j(document).on("click", "#label_19", function(){ // This is the changed line
alert("Clicked");
});
});
There, instead of hooking click on the actual element, we're hooking it on document but then asking jQuery to only tell us about clicks that pass through elements matching the selector we give it as they bubble. That way, the fact that something is destroying and recreating the #label_19 element doesn't matter, because we're not hooking a handler on that element. We're hooking the handler on document and checking, when the event occurs, if it passed through something that matches that selector.
But I wouldn't just blindly use event delegation, I'd find out what's really happening with that element.
Without seeing the rest of your code—including HTML and related DOM elements—have you considered using j(window).load() instead of j(document).ready()
var j = jQuery.noConflict();
j(window).load(function(){
console.log(j("#label_19"));
j("#label_19").on("click",function(){
alert("Clicked");
});
});
As explained here:
The window load event executes a bit later when the complete page is fully loaded, including all frames, objects and images. Therefore functions which concern images or other page contents should be placed in the load event for the window or the content tag itself.
I had a similar issue, it got resolved after i wrapped it in a window.load
(function ($) {
$(window).on('load', function () {
//MY click calls inside here
});
})(jQuery);
Hope it helps!
Im creating the same button in a loop as long as there is a certain condition, and I want to run the same jquery event handler for those buttons.
The problem is that the event handler only works for the first button generated.
My button is generated like this:
<?php while($row_c= mysqli_fetch_array($result_comments))
{ ?>
<button name="comment" id="comment" class="button_comment" value="Post">Post</button>
<?php } ?>
and the jquery im using is this:
$('.button_comment').click(function()
{
//some staff thats done here
});
What should do, so that the event handler works for all buttons that are generated?
Thank you
Your jQuery should work. Note that your buttons form invalid HTML/DOM: The id on an element must be unique. But since you're also using a class (which doesn't have to be unique), and that's what you're using in your jQuery selector, that works: Live Example | Source
Perhaps when you were trying it and it wasn't working, you were using $("#comment").click(...), which would not work, because of the id issue.
Check in firebug:
var buttoncomments = $('.button_comment');
console.log(buttoncomments);
Bind the control in question to a variable so you're certain that it's that one you're working with.
$('.button_comment').click(function()
{
var that = $(this);
"What should do, so that the event handler works for all buttons that are generated?"
A Fiddle would be a good way to show us the context of your problem ;)
Your code is valid because it will attach a handler to every element having 'button_comment' as a css class
Did you put this code inside a dom ready function ?
$(function(){
/* code here */
});
You can use event delegation
This mean you have to attach the event listener to the first common ancestor of those buttons. This is a good choice when many elements have to trigger the same routine an even more if some of them may be added to the DOM in the futur via AJAX (This keep the page from having to many event handlers attached to it)
Delegation syntax
// Event delegation
$( "#Ancestor").on("click", '.button_comment', function() {
alert( $( this ).val() );
});
Take the ID off from your php code. Remember ID's should be unique elements. Also, change your jquery from:
('.button_comment').click(function()
{
//some staff thats done here
});
to
('.button_comment').on('click', function()
{
//some staff thats done here
});
The on event will perform what you tell it to do at the time you tell it to do, so it does it pretty much at runtime. If you use your previous code, the element must exist at page load time for it to work.
I am creating some elements and appending it, and its working fine but when I want to call any function or want to call any jquery that not work, but when i put that elements directly instead of appending then it works properly. In all to say that appending element does not call any function or anything.
JQUERY CODE:
var cart_content = jQuery($.create('li', {}, [])).append($.create('span',{}, [av_title]),$.create('img', {"src":"images/delete_icon.png", "class":"cart_content", "alt":"delete", "title":"Delete"}, []));
$(".advertise_cart").append(cart_content);
$(".cart_content").click(function(){ alert("Hello"); });
<ul class="advertise_cart" id="advertise_cart_id">
<li>
<span>Inner Content 1</span>
<img src="images/delete_icon.png" class="cart_content" alt="delete" title="Delete"> <!------ On clicking on this will show alert box, but on clicking on appended element will not call alert box or anything ----->
</li>
</ul>
Thanks in advance
The problem you're experiencing is the result of the elements not being present in the DOM when the events are bound. To work around this, you can use delegate() or live():
$('body').delegate('.cart_content','click',
function(){
alert('hello');
});
JS Fiddle demo.
References:
live().
delegate().
Do not use .click function.
Use live function it work for newly added element in DOM
like this :
$(".cart_content").live(function(){ alert("Hello"); });
Using the live function to handle your event might help.
The only thing I'd add is that the live function means that the handler will continue to be applied to content that matches the selector at any point in the future (until and unless you call unbind). That is probably what you want here. If it isn't, you could write could that would add the click handler after a brief delay (say, 1.5 seconds). Or to be a little more sure, you could write code that would check for the presence of .cart_content every 100 milliseconds until it's found, and then add the click handler. So this approach would add the click handler only once. If something caused another .cart_content were added later, the click handler would not automatically be added.
I have a simple link:
Test Link
I want to get an alert whenever this link is pressed, so I add:
<script>
$('#test').click(function() { alert('clicked!'); } );
</script>
and it works fine, but when i move this code to a remote javascript file, it doesn't work..
any idea why?
I've also tried this code:
$(document).ready(function() {
$('#test').click(function() { alert('clicked!'); });
});
Your second example, using the ready function, should be working. Your first example should also work provided you include the script below the element with the ID "test" (the element has to already exist when your script runs, since you're not waiting for DOM ready). In both cases, your script must be included below (after) the jQuery script.
Example when you don't use ready
Example when you do use ready
I'd check that your external file is actually getting loaded (look for 404 errors in the browser console).
Update: From your comment below, the problem is that the "test" element doesn't exist when you're trying to hook up the handler. click only sets up the handler on the element if it already exists. If you're creating the element later, you have three options (two of which are really the same):
Use the code you already have, but run it after you've created the element (e.g., in the success callback of the ajax call you're making).
Use live, which basically hooks the click event document-wide and then checks to see if the element you tell it ("#test", in this case) was clicked.
Use delegate on the appropriate container (the element within which you're adding "test"). delegate is a more targeted version of live.
live and delegate are both examples of a technique called event delegation, which jQuery makes easy for you by providing those methods.
See the links for further information and examples, but for example, suppose you're going to be adding the "test" element to an element with the ID "target". You'd use delegate like this:
$("#target").delegate("#test", "click", function() {
alert("Clicked");
});
That hooks the click event on "target", but acts a lot like you've just magically hooked it on "test" as soon as "test" was added. Within your handler, this refers to the "test" element just as with click.