Why does event bubbling not work? - javascript

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.

Related

Handling the eventlistener

I'm working on my college's project and it's kind of like a web-text-based game. So I'm interested in a click event on a document to change the context and I did it with the code below.
The problem is that it keeps repeating everything and wont allow typing in the input.
const homepage = document.querySelector('#homepage')
document.addEventListener('click', function() {
/*I console.log to check that the function is still repeating*/
console.log('check')
homepage.innerHTML = `<div> hello what's your name? </div>`
document.addEventListener('click', function() {
homepage.innerHTML = `<div> my name is <br>
<input id="name"> </input> <br>
<button> submit </button<
`
})
})
#homepage {
text-align: center;
}
<div id="homepage"> click to change the content </div>
I will try to explain what is the exact issue that is happening below.
Here is your code
const homepage = document.querySelector('#homepage')
// You are adding a click event listner to your DOM
// This will trigger when ever you click on your html page
document.addEventListener('click', function () { // Code Section 1
console.log('this is a console log from first click event listner');
homepage.innerHTML = `<div> hello what's your name? </div>`;
// You are adding an another click event listner to your DOM
document.addEventListener('click', function () { // Code Section 2
console.log('this is a console log from second click event listner');
homepage.innerHTML = `<div> my name is <br>
<input id="name"> </input> <br>
<button> submit </button<
`;
})
})
#homepage {
text-align: center;
}
<div id="homepage"> click to change the content </div>
You have added a click event listner to your document using
document.addEventListener('click', function () {
(Refer Code Section 1 from the comment added in above code).
What this will do?
This will execute the entire set of code which is written inside that block when ever you click any where on your html page.
What is happening inside that code block?
Inside that code block, you are adding an another click event to the document using
document.addEventListener('click', function () {
(Refer Code Section 2 from the comment added in above code).
What happened till now?
You are now adding a click event listner, when ever the user clicks on the html application.
So what does this means?
This simply means that your code will keep on adding new click event listners whenever the user clicks on the application. So if the user click one time on the app, there will be two click event listner. If the user click the third time, one more event listner will be added, so that the total event listner will be three. This will keep on incrementing. This is happening because, you are keep on adding new click event listner when the pervious event listners exist.
So What wrong did happen? How to stop preventing the accumulation of event listners?
You have to revove the event listners before adding new event listners. You can make use of EventTarget.removeEventListener() for this purpose.
Implemetation using EventTarget.removeEventListener()
const homepage = document.querySelector('#homepage');
function listnerFunction() {
console.log('first click event triggered');
homepage.innerHTML = `<div> hello what's your name? </div>`;
// Removing the first event listner
document.removeEventListener('click', listnerFunction);
// Adding second event listner
document.addEventListener('click', secondListnerFunction);
}
function secondListnerFunction() {
console.log('second click event triggered');
homepage.innerHTML = `<div>
my name is <br>
<input id="name"> </input> <br>
<button> submit </button>
</div>
`;
// Removing the second event listner
document.removeEventListener('click', secondListnerFunction);
}
// You are registering click event to your complete html
document.addEventListener('click', listnerFunction);
#homepage {
text-align: center;
}
<div id="homepage"> click to change the content </div>
Explantion
Here I have added a click event listner function to the document using
document.addEventListener('click', listnerFunction);
What this will do?
This will trigger the function listnerFunction when the user clicks on the document.
Handling the second click from the first click event listner
Inside the first click event listner listnerFunction I have updated the innerHTML of your selected element. After this I have removed the first click event listner with
document.removeEventListener('click', listnerFunction);
There after I registered second click event listner with
document.addEventListener('click', secondListnerFunction);
What happens inside second click event listner?
Inside the second click event listner, I updated the innerHTML of your required target and I have removed the click event listner using
document.removeEventListener('click', secondListnerFunction);
This removing of the click event listner is required, after this whenever the user clicks the document, no events will be triggered and hence the user can use the app seamlessly.
const texts = [
"How are you?",
"Tired of studying?",
"Too long enough"
];
let textTake = 0;
document.addEventListener('click', ()=>{
if(textTake >= texts.length) textTake = 0; ​
document.getElementById("homepage").innerText = texts[textTake];
textTake++;
});
Here is a starter for you:
const homepage = document.querySelector('#homepage');
let html = [
"hello what's your name?",
'my name is <br> <input id="name"> <br> <button> submit </button>'
];
document.addEventListener('click',function(){
if (html.length) {
homepage.innerHTML = html.shift();
};
});

Trigger Click of Submit Button When Parent Is Clicked with jQuery

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.

Parent click events overlaps children click events

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

Form Submit Not Triggering When Modifying Form In focusout Handler

I have tried to boil this down to a simple example to demonstrate what I'm running into.
I have a form with an input field and a submit button:
<form id="focusOutAfterInputForm">
<input type="text" id="focusoutAfterInput">
<input type="submit" >
<div></div>
</form>
The input field has a focusout handler attached that inserts a new element below the input:
jQuery("#focusoutAfterInput").focusout(function() {
jQuery(this).after("<div>message</div>");
});
I have also attached a submit handler just to capture if the submit is run:
jQuery("form").submit(function() {
jQuery("#console").append("<li>submitted form" + this.id + "</li>");
return false;
});
If the cursor is placed into the input and the submit button is clicked, the submit handler does not fire. If the submit button is clicked a second time it will fire. Also, if the field is blur'ed and then the submit button is pressed it will fire.
However, if instead we insert the new element below the div in the form, the submit button will fire even though the element is inserted:
<form id="focusoutAfterDivForm" novalidate="novalidate">
<input type="text" id="focusoutAfterDiv" required="true">
<input type="submit" >
<div></div>
</form>
jQuery("#focusoutAfterDiv").focusout(function() {
jQuery(this).parent().last().after("<div>message</div>");
});
Here is a jsfiddle demonstrating the code. I'm a bit baffled. Ideas anyone?
The problem doesn't seem to be that submit isn't executed, but rather the click event isn't executed on the button, because the mouseup isn't on the submit button once the div is inserted. If you use tab and space in your fiddle it works. Also if you add the element in a way that the submit button doesn't move, it works:
jQuery("#focusoutAfterInput").focusout(function() {
jQuery(this).next().after("<div>message</div>");
});
or in this example: http://jsfiddle.net/K9vrW/3/

Submit Event Listener for a form

I've written an event listener for a form submit that is causing me a few issues. When pressing 'enter' inside the text field everything works fine. However, I have an span (with background-image) that submits the form as well via a click event. This is not working properly and I can't figure out why.
Here's the basic HTML:
<form name="myForm">
<input type="text" name="search" />
<span id="search-button"></span>
</form>
Here's the JS for the event listener:
function evtSubmit(e) {
// code
e.preventDefault();
};
var myform = document.myForm;
if (myform.addEventListener) {
myform.addEventListener('submit', evtSubmit, false);
}
And here's the JS for the 'span' and its click event:
var searchButton = document.getElementById('search-button');
if (searchButton) {
searchButton.onclick = function() {
document.myForm.submit();
};
}
NOTE: The JS for the span's click event is in a separate JS file and inaccessible at the moment, so changing that script is less of an option. If the only way to fix this issue is to update that file, I can... but due to processes beyond my control, it's much more difficult.
When calling form.submit(), the onsubmit event won't be triggered. As an alternative, you can set the action attribute to javascript:evtSubmit(event):
function evtSubmit(e) {
// code
e.preventDefault();
};
var myform = document.myForm;
myform.setAttribute('action', 'javascript:evtSubmit();');

Categories