jQuery click event not firing when appending new button - javascript

When I click the 'Show' button the show listener is invoked and a new hide button is displayed. But why is the hide button not invoked when I then click 'Hide' ?
$('.myCss').append('<input type="button" class="show" value="Show"/>');
$('.show').on('click', function () {
console.log('show clicked');
$('.myCss').append('<input type="button" class="hide" value="Hide"/>');
});
$('.hide').on('click', function () {
console.log('hide clicked');
$('.myCss').append('<input type="button" class="show" value="Show"/>');
});

It has to do with the order of elements being added to the page. If you drop the hide code within the show code it works (although you should check your logic):
$('.show').on('click', function() {
console.log('show clicked');
$('.myCss').append('<input type="button" class="hide" value="Hide"/>');
$('.hide').on('click', function() {
console.log('hide clicked');
$('.myCss').append('<input type="button" class="show" value="Show"/>');
});
});​
jsFiddle example
In your original code, the code to bind the click event to the hide button exists prior to the actual hide button, so it isn't actually bound to anything. By moving it within the other code block you delay the execution of that chunk. You could also use .on() to bind the click event to an event higher up in the DOM but it's really basically the same final result.
From the docs:
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().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page. Or, use delegated events to attach an event
handler, as described next.

Because when you set the event, the .hide element doesn't exist.
You could try setting the events like:
$('.myCss').append('<input type="button" class="show" value="Show"/>');
$('.myCss').on('click', '.show', function () {
console.log('show clicked');
$('.myCss').append('<input type="button" class="hide" value="Hide"/>');
});
$('.myCss').on('click', '.hide', function () {
console.log('hide clicked');
$('.myCss').append('<input type="button" class="show" value="Show"/>');
});
This attaches the click to the .myCss element (shich always exists), but only fires the function when the click was fired on .hide element inside it.
This solution is more efficent that creating the event everytime the element is created.

the problem is that you "hide" button does not exist when you try to define the onclick event on it.
I would suggest that you add it, set display=none, and then show it

When the '.hide' event handler is created, the 'Hide' button doesn't exist yet.
You could set up the event handler after creating the element, use event bubbling, or use use .live.

Several people have correctly answered that the hide button is created after the event is bound to the controls.
I suggest a different approach of using a handler at a higher level (document, in my example) that will attach to future controls (the current approach to the now deprecated .live).
Example:
http://jsfiddle.net/kQ2JA/1/
This will better match your expectations of binding the event to all current and future controls.

Related

Stop a dynamic bound eventhandler to fire immediately

I'm using jQuery 2.1.3. I try to build a button that will show/hide content on click. I also want to hide the content when there's a click on a wrapping container (in my application it's the document itself, but I used a regular div in my example below). As there is some more logic to handle I'm also making use of custom events that I fire via .trigger().
This works fine but I have a problem. When I show the hidden content I apply an eventlistener to document to hide the content when the user clicks anywhere on it. This eventhandler is fired straight after the content is shown resulting in my content gets hidden immediately.
I can only get around this by using $evt.stopPropagation() in my toggle buttons event handler. But that's no solution for me as I need the event to bubble up as other elements listen for that.
Heres the simplified HTML:
<div id="document">
<button id="btn" type="button">Toggle</button>
<div id="content" class="hidden">Hidden Content</div>
</div>
And heres the JS to this:
//trigger custom show event
var show = function () {
$("#document").trigger("show");
};
//trigger custom hide event
var hide = function () {
$("#document").trigger("hide");
};
//handle custom show event
$("#document").on("show", function () {
$("#content").removeClass("hidden");
$("#document").on("click", hide);//add listener to container, fires immediately
});
//handle custom hide event
$("#document").on("hide", function () {
$("#document").off("click", hide);//remove listener from container
$("#content").addClass("hidden");
});
//on toggle button click call custom events
$("#btn").click(function ($evt) {
//$evt.stopPropagation(); would work but event should propagate
$("#content").hasClass("hidden") ? show() : hide();
});
JSFiddle
What could I do about this? Stopping event propagation on toggle button click would show my content but i need this event to bubble up as there are other elements around waiting for that. How could I avoid that the applied event listener on document fires immediately?
PS: jQuerys .show()/.hide() functions are no alternative for me.

Jquery stop event from triggering

I have simple scenario as shown in fiddle which is
Initially event is binded on only one button A and click event is triggered in both the buttons(A & B) manually.
Now in event handler of button A the event handler for button B is binded. It is expected not to execute the callback of B as it is binded after the event is triggered.
But the callback is still executed. Where am i going wrong?
HTML:
<div>
<button class="a">botton A</button>
<button class="b">button B</button>
</div>
JS:
$(function () {
$('div').on('click', '.a', function (e) {
e.preventDefault();
$('div').on('click', '.b', function () {
alert('Can"t see me')
});
});
$('button').trigger('click');
});
FIDDLE LINK
EDIT:
I have this scenario in my project and
now I knew why is this happening :). But how can i stop event from propagating?
$('button').trigger('click'); triggers a click event on both buttons in the order they appear in the document markup. Since event bubbling is synchronous, the event for .b is binded before the click event is triggered on that button. Here's the breakdown:
$('button') creates a collection of button .a and button .b.
.trigger('click'); iterates through each button in the collection and generates a click event.
button .a receives the click event and runs its event handler you registered for it on page load.
The event handler for .b is registered within the callback of the event handler for .a.
button .b receives the click event from .trigger('click'); since it's second in the collection.
The callback of the event listener for button .b triggers the popup alert.
Since you only want to trigger button .a to be clicked, any of the following will work on your current document:
$('.a').trigger('click');
$('button.a').trigger('click'); (though this is redundant)
$('button').get(0).trigger('click'); (since .a is the 0th indexed button element)
EDIT Although this is unrelated to the question, Perhaps you meant to register the event for .b only once, doing this:
$('.a').one('click', function (e) {
$(function() {
$('.a').one('click', function(e) {
e.preventDefault();
$('div').on('click', '.b', function() {
alert('Can\'t see me');
});
});
$('.a').trigger('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<button class="a">button A</button>
<button class="b">button B</button>
</div>
SECOND EDIT If you want to stop event propagation, you need to add another event listener, like this:
$(function() {
$('.a').one('click', function() {
$('.b').on('click', function(e) {
e.stopPropagation();
alert('Can see me');
});
$('div').on('click', '.b', function() {
alert('Can\'t see me');
});
});
$('button').trigger('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<button class="a">button A</button>
<button class="b">button B</button>
</div>
It's because of $('button').trigger('click');.
This triggers a click on every button - first it clicks on .a, which binds the click handler to .b, then it clicks on .b.
In my experiment, simply putting button .b first fixed this, but I wouldn't rely on that.
This is probably because the click is being triggered on all button instances serially...
button a click gets executed first...in its callback you bind button b click callback...
...then...button b click gets executed...hence callback for b gets executed...
All of this happens in that one call: $('button').trigger('click');
Edit: others have probably answered this before I have...but to iterate trigger the click on the correct element :
$('.a').trigger('click');
The order of the execution is:
bind .a
trigger first button (say, a)
callback for a: bind b
trigger second button (b)
callback for b
Even $("button").trigger("click") is a single line, it is a kind of loop, works like if it were:
var buttonElms = $("button");
for (var index in buttonElms) {
buttonElms[index].trigger("click");
}

Registering an existing event to ajax loaded DOM?

In my homepage , I have this button.
<button class="test">test</button>
And in my current code I have this script
$('.test').on('click',function(){
alert("YOU CLICKED ME");
});
Now, my application is ajaxified, so everytime I click a new page it is loaded as ajax, the problem is that the loaded page also has this button. and its markup is likethis
<div id ="firstDiv>
<div id ="secondDiv">
<button class="test">test</button>
</div>
</div
So the new content also has "#test" but how come when I click that button it does not execute the event handler I created?
var $bdy=$(document.body);
$bdy.on('click','.test',function(){
alert("YOU CLICKED ME");
});
now append your .test anytime you like
So the new content also has "#test" but how come when I click that button it does not execute the event handler I created?
Because the handler is attached to the actual element. So if the element is removed and a new element with the same class is created, the event is not associated with that new element.
You could use event delegation to handle this:
$(document.body).delegate('.test', 'click', function(){
alert("YOU CLICKED ME");
});
or
$(document.body).on('click', '.test', function(){
alert("YOU CLICKED ME");
});
(They do the same thing, note the order of arguments is different. I prefer delegate for the clarity, but I think most people use the delegating version of the far-too-overloaded on method instead.)
What that does is watch for the click on document.body, but only fire your handler if the click passed through an element matching that selector (.test, in this case).
As you said that the content is loaded through data you get in AJAX this is the possible scenario that is happening.
<button class="test">test</button> is drawn
Then it is binded to to click event
You load the new data through ajax
Try to bind that it does not.
This is because when you first bind the click event to "test" element with that class are part of the DOM. Now that you add some markup after ajax call the elements become the part of DOM, but now after you wrote the new markup you need to first unbind the click event See Here. And then re-bind the click event. This will bind the event to all elements having class "test".
P.S. I don't know the specifications of your implementation but as others have suggested you should bind events to id and not class.
I finally found out the solution. all I needed to do was define a static container which is this
$('#staticdiv').on('click','.test',function(){
alert("YOU CLICKED ME");
});
and that fixed the issue

button click event triggering wrong event handler

I have a div display some titles of music which is clickable. When you click it it will show some more detail information. Then I also have a button in the clickable div. When I click the button. It won't call the function of the button but the function of the div? Is there a way to solve this? Thank you!
$("#myComList").append("<div id="+comListID+" class=listDiv> <p class=comTitle><? echo $row["compositionTitle"] ?>(<?echo $row["year"]?>)</p><button id="+comListID+"btn class=addNewArrBtn>Add new Arrangement</button> <p class=comOri>BY <? echo $row["OrigComposer"] ?></p> </div>");
$('#'+comListID).click(function() {
$(this).find("li").slideToggle('slow', function() {
});
$("#"+comListID+"btn").click(function(){
addNewArr(comListID);
});
It's called 'bubbling'. The button is inside the div so it's executing button then up the chain to div. Add event.stopPropagation() inside the button function.
$("#"+comListID+"btn").click(function(event){
event.stopPropagation();
addNewArr(comListID);
});
From jQuery documentation:
By default, most events bubble up from the original event target to
the document element. At each element along the way,
jQuery calls any matching event handlers that have been attached.
A handler can prevent the event from bubbling further up the document
tree (and thus prevent handlers on those elements from running) by
calling event.stopPropagation(). Any other handlers attached on the
current element will run however. To prevent that, call
event.stopImmediatePropagation(). (Event handlers bound to an element
are called in the same order that they were bound.)
http://api.jquery.com/on/
So you'd call event.stopPropagation() inside the button click handler, as to stop the div event from firing.
I believe I understand your question without seeing the code. The problem it sounds like stems from the click event bubbling or propagating up. Below is a sample of code to try and a link to a fiddle for you to test:
<div id="testDiv" onclick="alert('Stop Clicking Me!');">
<button type="button" onclick="function(e) { alert('You hit the button!'); e.stopPropagation(); }">Click Me!</button>
</div>
In this function, the:
e.stopPropagation();
prevents the click event from filtering up to its parent container (in this case "testDiv") and triggering its click event as well. You can test it and see for yourself in the jsfiddle below:
Test Fiddle
EDIT:
In your case:
$("#"+comListID+"btn").click(function(e){
addNewArr(comListID);
e.stopPropagation();
});
add the event parameter to the click function and stop it from propagating to the parent.
Hope this helps.

Each submit button calls the same function

I'm calling function for selecting component ID after page refresh:
$(document).ready(
function() {
document.getElementById('form:#{myBDE.componentId}').focus();
document.getElementById('form:#{myBDE.componentId}').select();
}
)
My submit button example:
<h:form id="form">
<h:commandButton id="btnSubmit" action="#{myBDE.save}" type="submit" >
<f:ajax execute="#form" render="#form"/>
</h:commandButton>
...
</form>
How can I create the same function to be called each time I click any submit button (I'm using ajax, so I'm working without page reloading, document.ready is not enough for me). Is it possible?
update (partially functional solution):
var myFunc = function() {
document.getElementById('form:#{myBDE.componentId}').focus();
document.getElementById('form:#{myBDE.componentId}').select();
};
$(document).ready(function() {
myFunc();
$('input[type=submit]').click(myFunc);
});
I can call the function adding onclick="return myFunc();" to h:commandButton, problem is, that <f:ajax> render the form after calling the function, so the select and focus is cleared :(
Give the function a name (currently, its an anonymous function), and then call the function as and when you need.
$(document).ready(
onPageLoad(); // call it when page loads
$('form').submit(onPageLoad); // also call whenever a form is submitted
)
function onPageLoad() {
document.getElementById('form:#{myBDE.componentId}').focus();
document.getElementById('form:#{myBDE.componentId}').select();
return true;
}
This should work
$('input[type="submit"]').click(function(){
document.getElementById('form:#{myBDE.componentId}').focus();
document.getElementById('form:#{myBDE.componentId}').select();
});
In fact you can bind a function to any events. In this case it is the click event for the input tag with the attribute type=submit
You should delegate the handler to a higher-level dom element:
$(document).ready(function() {
$('h:body').on('click', 'input[type=submit]', function() {
document.getElementById('form:#{myBDE.componentId}').focus();
document.getElementById('form:#{myBDE.componentId}').select();
});
});
See jQuery.on().
When a selector is provided, the event handler is referred to as
delegated. The handler is not called when the event occurs directly on
the bound element, but only for descendants (inner elements) that
match the selector. jQuery bubbles the event from the event target up
to the element where the handler is attached (i.e., innermost to
outermost element) and runs the handler for any elements along that
path matching the selector.
And then later:
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.
So the event will be handled for all elements of that type, even if they are added later via ajax. Note that I used h:body here as the element to delegate to but you can use any element that exists at document.ready time and is guaranteed to be an ancestor of all submit inputs.
If you have different ID or Name on them it's just to do somthing like
$(document).ready(function(){
$('#btn1, #btn2, #btn3').on('click', function(evt){
evt.preventDefault();
//your code
$(this).submit(); // or not submit, your choice
});
});
Try this thing
$(document).ready( function (){
$('input[type=submit]').click( function (){
// Whatever you want on submit button click option.
}
});
This will grab every submit button on the page and will bind click event to it, and this code will be called on any submit button click.

Categories