I want to be able to click the parent div, but have the child form and anchor elements be exempt from the click event.
<div class="root">
<div class="parent">
<div class="container">
Hello World
<div>
<span> Hello World Again
</div>
<form class="form">
<label> inputs
<input id="text" type="text"/>
<textarea></textarea>
<input id="submit" type="submit"/>
</form>
</div>
</div>
</div>
jQuery:
$(".parent").on("click", ".container", function() {
alert("Parent clicked");
});
$(".parent").on("click", "#text", function(event) {
event.preventDefault();
alert("Form Input clicked");
});
$(".parent").on("click", "#submit", function(event) {
event.preventDefault();
alert("Form Button clicked");
});
$(".parent").on("click", "a", function(event) {
event.preventDefault();
alert("anchor link clicked");
});
Clicking on any of the form elements (input, textarea, button submit) or the anchor tag won't work because the parent click event is firing.
How can I make the parent click event only on elements outside of the child form and anchor tag? The parent click event can cover any of the other children except those two elements.
You have two choices:
Hook the click event on the relevant child container (it looks like it would be the form) and use event.stopPropagation() within the handler. That will stop the event bubbling to the parent. Something like this:
// Option 1: Hook on the form and stop
// propagation (hooking up "false" does an
// implicit preventDefault + stopPropagation)
$(".parent").on("click", "form", false);
Inside the parent's handler, check to see if the parent element is the element that was the source of the event (e.target). If it is, process the event; if it isn't, ignore it, it was generated on a child element. Something like this:
$(".parent").on("click", ".container", function(event) {
// Option 2: Look at event.target and ignore
// the event if it originated in a descendant
// element
if (this === event.target) {
alert("Parent clicked");
}
});
Note that those do two slightly different things. The first one says "Prevent the parent from seeing clicks on the form" whereas the second one says "Ignore clicks that aren't directly on .parent .container". Either can be adapted, of course, with different targeting.
Live Examples:
Your original problem
Option 1
Option 2
Related
I thought I understand event bubbling but my code does not work as expected:
<div id="write">
<form method="post" tabindex="1" onsubmit="myfunc()">
<textarea></textarea>
<input />Another form element
<input />Another form element
<input type="submit">
</form>
</div>
<p>Some other text and images...</p>
<div id="overlay"></div>
<script>
//var formele = document.querySelectorAll("#write form, #write form *");
var formele = document.querySelectorAll("#write form");
for (var i = 0; i < formele.length; i++) {
formele[i].addEventListener("focus", function () {
overlay.classList.add("active");
});
formele[i].addEventListener("focusout", function () {
overlay.classList.remove("active");
});
}
</script>
This code should darken the rest of the page when the form or an element inside gets focus or is clicked. I understood event bubbling in the following way:
When a child of the form gets focus it has no event listener added. Thus the focus event bubbles up to the form element and triggers it's event listener.
However it doesn't work that way: If I add the event listener to the form only nothing happens when a textarea or input inside is clicked. I have to add event listeners to the child elements too in order to get it work.
Can anyone explain? Obviously my understanding of event bubbling is wrong somehow.
I have a dead simple search form:
<div>
<input id="searchArea" type="text" value="" placeholder="Enter your search terms">
<button id="searchButton">Search</button>
</div>
and a snippet of Javascript controlling it:
function searchFunction() {
console.log("This is a POST request being send to the server");
}
$("#searchButton").on("click", function(e) {
e.preventDefault();
searchFunction();
})
$("#searchArea").on("change", function(e) {
e.preventDefault();
searchFunction();
});
The problem is that click and change overlap in functionality and so I get the following:
When I type something in the text box and then click elsewhere in the screen, searchFunction is fired properly but if "elsewhere" becomes the search button, then searchFunction double fires.
My question is this: Once inside the click handler, is there any way to cancel the change handler from firing as well which would otherwise cause searchFunction to double fire?
Mind that searchArea and searchButton don't have a parent-child relationship which means conventional methods like preventDefault and stopPropagation won't work.
You can see it in action in this fiddle.
$(":input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
registerButtons();
});
function registerButtons() {
$("#order").on("click", function(e) {
console.log("click triggered");
alert("Hello World");
});
$("#order").on("mousedown mouseup", function(e) {
console.log(e.type + " triggered");
});
}
registerButtons();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" value="123"/>
<div id="section">
<button id="order">Order</button>
</div>
I have a web page with a button and some input fields.
On the button an click event is registered
On the input fields an change event is registered
The onChange will trigger an AJAX server call, and the result will replace parts of the web page - including the button. After AJAX result is processed, all listener are registered again.
Now the problem. A user changes the value of an input field, and clicks directly the button - but to slow (lets assume the user needs 500ms for the click), so the onChange event is fired and the page is "updated/replaced". Now the "old" button fires an onMouseDown and the "new" button fires an onMouseUp event - but no onClick.
My current workaround is, to register the two mouseDown/mouseUp events, get the timestamp of the mouse down, and if the mouse up comes in 2 seconds, do what should be done by the onClick.
It is no option to remove the button part from the AJAX response - in worst case the button could be removed and replaced by an user info.
My hope is, that there is a better solution... any ideas?
You can take advantage of the event delegation and set your listener on the container instead of the button.
You are adding a click listener to your old button and your adding a new button to the dom. So the click won't work.
The button wasn't working because for some reason it can't focus when you hover over it. So I added a getFocus method and now it should work.
$("input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
});
function registerButtons() {
$('#section').on("mouseup", '#order', function(e) {
alert('Clicked!');
});
}
registerButtons();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" value="123"/>
<div id="section">
<button id="order">Order</button>
</div>
I just found out that jQuery provides a sweet API that can be used for event delegation. This way we don't have to manually check for event target. Check it out http://api.jquery.com/on/
$("input").on("change", function(e) {
console.log("change triggered");
$("#section").html("<button id='order'>Order</button>");
});
function registerButtons() {
$("#section").on("click", '#order', function(e) {
console.log("click triggered");
alert("Hello World");
});
$("#section").on('mouseover','#order', function(e){
$(this).focus();
});
}
registerButtons();
I want to replace a button with an input field, where the user enters something and presses the enter button. After that, the button from the beginning should appear again. My script works so far but I can't repeat this once it finished.
Update: The button should also appear again, if the input field is shown but the user don't want to enter anything and clicks somewhere else.
The code:
<button id="createButton">Create item</button>
/*
jquery stuff
*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#createButton').click(function( event ) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
$(this).on('keypress', function (event) {
if(event.which == '13'){ // If enter button is pressed
alert('You entered something');
$('#buttonInput').replaceWith('<button id="createButton">Create item</button>');
}
});
});
</script>
Update 2: I updated the code with hide() and show() to get the same result. But how can I let the input disappear, if the user clicks somewhere inside the body, without redundancy?
The new code:
<button id="createButton">Create item</button>
<input type="text" id="input" placeholder="e.g. books, movies" autofocus>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
$('#input').hide();
$(document).on('click', '#createButton', function (event) {
$(this).hide();
$('#input').show().focus();
});
$('#input').on('keypress', function (event) {
if (event.which == '13') { // if enter button is pressed
$(this).hide().val('');
$('#createButton').show();
}
});
});
</script>
As the other answers say, you're replacing the element (createButton), which means the click handler is no longer bound.
You can either re-bind, or bind to the parent element, with the #createButton selector using on.
$(document).on('click','#createButton', function( event ) {
...
});
Don't actually use document - use whatever the parent element is, which doesn't get replace (a div, perhaps?)
Replacing DOM elements is a bad approach though -- you'd be better off leaving the elements on the page, and using show and hide.
http://jsfiddle.net/v03j8bns/
Updated Answer
Here's a fiddle showing the show/hide/ approach. To handle:
The button should also appear again, if the input field is shown but the user don't want to enter anything and clicks somewhere else.
When the button is clicked, I call focus() on the textbox. I've also hooked up a blur() event handler, so if the user clicks/tabs out, then it'll hide the textbox and show the button.
You have to bind the click event to newly created button again:
$(document).ready(function () {
$('#createButton').click(function (event) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
$(this).on('keypress', function (event) {
if (event.which == '13') { // If enter button pressed
//Disable textbox to prevent multiple submit
alert('You entered something');
$('#buttonInput').replaceWith('<button id="createButton">Create item</button>');
}
$('#createButton').bind('click', function (event) {
$(this).replaceWith('<input type="text" id="buttonInput" placeholder="e.g. books, movies" autofocus>');
});
});
});
You have this issue because you replace DOM elements. It means that your new element button no longer has click handler.
I would recommend you to use something like show/hide or use jQuery delegate on/bind for handling click.
When you're changing DOM on the fly and want to automatically assign listeners to elements that may or may not exist at certain points of time, you need to use delegated event listeners.
$(document).on('click', '#createButton', function () { ... });
$(document).on('click', '#buttonInput', function () { ... });
These handlers will work however you scramble the DOM.
I have a submit button wrapped in a div. I want to trigger a click event on the submit button when the div is clicked. The problem is, this click event bubbles up and causes a stack overflow/infinite loop. When I try to stop propagation of the child click event, the issue is not resolved.
HTML:
<div class="container">
<input type="submit" value="OK">
</div>
jQuery:
$('.container').click(function () {
var input = $(this).find('input');
input.trigger('click');
input.click(function(event) {
event.stopPropagation();
});
});
This still causes the stack overflow error. So does returning false:
$('.container').click(function () {
var input = $(this).find('input');
input.trigger('click');
return false;
});
Help appreciated!
Don't bind button click event inside container click one. It should be this way
$('.container').click(function() {
var input = $(this).find('input');
input.trigger('click');
});
$('input').click(function(e) {
e.stopPropagation();
alert('button clicked');
});
The problem with your code is that container click triggers inner button click before its respective click handler is even registered, so it have no chance to stop event bubbling.