addEventListener not working on document.createElement elements - javascript

I'm trying to build a simple signup form that does some basic validation on the username, including checking whether a given user name is taken and if so, a list of suggested usernames. A problem that I ran into was that the dynamically generated list of suggestions (anchor tags) won't take an event listener for some reason. Here is the sample code of this.
singup.html
<script type="module">
import { check_username } from './signup.js';
const username_input = document.getElementById('id_username');
username_input.addEventListener('change', (event) => {
check_username(event, check_url);
}, false);
</script>
signup.js
export const check_username = async (event, url) => {
...
// check username with server
if (!data.unique) {
error_flag = true;
error_p.innerHTML = 'Username taken, please consider:<br>';
for (let name of data.suggestions) {
let child_elem = document.createElement('a');
child_elem.innerHTML = `${name}`;
error_p.appendChild(child_elem);
error_p.innerHTML += ' ';
child_elem.addEventListener('click', clickHandler, false);
}
}
}
According to some other posts, I tried adding the event listener both before and after the element has been added, i.e. before and after calling appendChild, but neither worked. Curiously, I tried adding the event listener in the browser console, and that worked like a charm. I ended up resolving this by adding the event listener to the body and checking whether the event.target is the element in question, but I'm curious to know why my initial approach didn't work to get a better understanding of things.

Setting the innerHTML of error_p replaces the content including all elements with their event handlers. Just insert a text node instead of a space with inner html
for (let name of data.suggestions) {
let child_elem = document.createElement('a');
child_elem.innerHTML = `${name}`;
error_p.appendChild(child_elem);
error_p.appendChild(document.createTextNode(' '));
child_elem.addEventListener('click', clickHandler, false);
}

Related

Does anyone know why this code is not working the way I want it to?

I am creating a web app with node.js, express and pug templates and here I am trying to simulate a warning when the user tries to remove a review he has posted.
so, in the front end I have a button that the user clicks to remove his review
when the user clicks that button I run
index.js
import { showWarning } from './warning';
const removerStoreReviewBtn = document.querySelector('.side-nav__removeStoreReviewbtn');
if (removerStoreReviewBtn)
removerStoreReviewBtn.addEventListener('click', e => {
e.preventDefault();
showWarning('Would you like to remove this review ?');
});
warning.js
export const hideWarning = () => {
const el = document.querySelector('.warning');
const warningText = document.querySelector('.warning__text');
if (el) el.parentElement.removeChild(el);
if (warningText) warningText.parentElement.removeChild(warningText);
};
export const showWarning = (msg, time = 30) => {
hideWarning();
console.log(msg);
const markUp = `
<div class="warning">
<div class="warning__text">${msg}</div>
<button class="warning--no">
<span>Cancelar</span>
</button>
<button class="warning--yes">
<span>Apagar</span>
</button>
</div>`;
document.querySelector('.header').insertAdjacentHTML('afterend', markUp);
window.setTimeout(hideWarning, time * 1000);
};
The showWarning function display everything the way I want in the front end
then back at the index.js file I have the following code
index.js
const warningBtnYes = document.querySelector('.warning--yes');
const warningBtnNo = document.querySelector('.warning--no');
if (warningBtnYes)
warningBtnYes.addEventListener('click', e => {
e.preventDefault();
console.log('remove');
//removerStoreReview(reviewId);
});
if (warningBtnNo)
warningBtnNo.addEventListener('click', e => {
e.preventDefault();
console.log('Do not remove');
});
when I click any of these buttons nothing happens (I am expecting the console.logs) and I can't figure out why nothing happens, hopefully anyone can help me.
Thanks
Mateus
When you use .parentElement.removeChild() you have turned off all event listeners for those button.
You have two options. You can preserve the event listeners by storing the return value from the .removeChild() call. In order to restore the event listeners you will need to reuse the stored (previously removed) node.
Alternatively, you'll need to re-add your event listeners after inserting the new HTML.
Helpful docs

Make Javascript wait for an HTML element to exist [duplicate]

This question already has answers here:
How can I be notified when an element is added to the page?
(8 answers)
Closed 6 years ago.
I am trying to make a bot that sends virtual currency over to another user. I have the bot search through a database for users. Before searching, the inner html of a division has no elements at all. After searching, it is then filled with several user links.
Because it takes a short while for results to appear, I need Javascript to wait for at least one anchor tag to exist. How can I do this?
There are many, many better ways to do this, all of which stem from actually checking when the AJAX data populates the element itself, but the following will work:
var t = setInterval(function () {
if ($("element").children().length > 0) {
clearInterval(t);
// do stuff
}
}, 50);
Using setTimeout() to delay the code a few seconds is risky, since on older browser/machines it may take longer than expected.
Use promise() instead, You can find documentation https://api.jquery.com/promise/ .
Using onload event, You can use onload with tag a.
EX: http://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_img_onload
I'm guessing this is an AJAX call.
You could use AJAX callback to check if you got any results from the server.
Something like this:
var tags_available = false;
$.ajax({
... the ajax stuff;
}).done(function(data){ // The callback
if(data || $('#tags_element').lenght != 0){
tags_available = true;
}else{
tags_available = false;
}
})
Then:
if(tags_available){
console.log("Tags available")
}
If I've understood you correctly you need to check if dom element have been updated/populated with new elements. There are a few ways you can achieve that:
1.) Using window.setTimeout function
function checkForChanges() {
var observeThis = document.getElementById('observethis');
if (observeThis.hasChildNodes()) {
alert('yes');
return;
/*this is gonna execute only once */
}
window.setTimeout(function() {
checkForChanges();
}, 25);
}
checkForChanges();
/* this part is only relevant for demonstration.
It shows what happens when dom element gets new child */
(function() {
var observeThis = document.getElementById('observethis');
var button = document.getElementById('button-append');
button.addEventListener('click', function() {
var anchorElement = document.createElement('a');
anchorElement.href = "http://example.com";
anchorElement.target = "_blank";
anchorElement.innerHTML = "Link";
observeThis.appendChild(anchorElement);
}, false);
})();
<div id="observethis"></div>
<button id="button-append">append anchor</button>
2.) MutationObserver class
this is modern approach (I would also say recommended one).
function checkForChanges() {
var observeThis = document.getElementById('observethis');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'childList') {
alert("insert your own code");
}
});
});
var config = {
attributes: true,
childList: true,
characterData: true
};
observer.observe(observeThis, config);
//observer.disconnect();
//use observer.disconnect to end observations
}
checkForChanges();
/* this part is only relevant for demonstration.
It shows what happens when dom element gets new child */
(function() {
var observeThis = document.getElementById('observethis');
var button = document.getElementById('button-append');
button.addEventListener('click', function() {
var anchorElement = document.createElement('a');
anchorElement.href = "http://example.com";
anchorElement.target = "_blank";
anchorElement.innerHTML = "Link";
observeThis.appendChild(anchorElement);
}, false);
})();
<div id="observethis"></div>
<button id="button-append">Append Child</button>
Read more about MutationObserver here
3.) If you are just waiting to get a response from ajax callback and don't actually need to observe changes in dom then just use XMLHttpRequest. Or even better. Use new javascript fetch API (you are gonna need polyfill to ensure it works in most browsers)

Make an onclick event react different when click second time

I have a button on my website, which plays the music when you click on it and in the same time it changes the text inside of the button (to "Go to SoundCloud".)
I want that button (with the new text on it) to redirect to SoundCloud when I click on it.
Now I got both when click first time, which is redirect to SoundCloud and play the track. (plus it changes the text)
Any ideas, how to solve this problem? Thx!
var links = document.getElementById("playButton");
links.onclick = function() {
var html='<iframe width="100%" height="450" src="sourceOfMyMusic"></iframe>';
document.getElementById("soundCloud").innerHTML = html;
var newTexts = ["Go to SoundCloud"];
document.getElementById("playButton").innerHTML = newTexts;
newTexts.onclick = window.open('http://soundcloud.com/example');
};
Use a variable that indicates whether it's the first or second click.
var first_click = true;
links.onclick = function() {
if (first_click) {
// do stuff for first click
first_click = false;
} else {
// do stuff for second click
}
}
Just redefine the onclick after the first function call.
Put the onclick on the button instead of the html.
document.getElementById("playButton").onclick=window.open('http://soundcloud.com/example');
Another option in some cases is to use a ternary operator and a boolean toggle expression:
let btn = document.querySelector('.button');
let isToggledOn = false;
btn.addEventListener ('click', function(e) {
e.target.textContent = !isToggledOn ? 'Is ON' : 'Is OFF';
isToggledOn = !isToggledOn;
});
newTexts.onclick is not creating a function to open a window, it is simply taking the return value of window.open which is being executed right away.
It should look like:
newTexts.onclick = () => window.open('http://soundcloud.com/example');
Also this will not work as intended because newTexts is not the actual DOM element, you need to attach the new onclick on the element and not the array...
But to other answers in this page, the logic is hard to read, so I'd advise to refactor the logic to be more readable.

JavaScript - Detect whether a click event triggered a DOMNodeInserted event

I'm working on a chrome extension business gamification system that tracks users across various web applications, and tracks their progress within the applications to determine a user ranking in their workplace.
I know you can track which elements are created, using something like:
window.addEventListener('DOMNodeInserted', function(e){
console.log(e.target);
}, false);
But as this can be quite slow (noticeable lag within complex web applications), I would like to only detect elements that are created as a result of mouse clicks (on an element, for example).
I just tested this, locally:
<!DOCTYPE html>
<html>
<body>
<div id="creator">Click me</div>
<script>
var click_event = 0;
function add_element(e)
{
console.log(e);
click_event = e;
var div = document.createElement('div');
div.innerHTML = "created";
document.body.appendChild(div);
}
function element_created(e)
{
console.log(e);
if(e == click_event)
{
console.log("SAME EVENT");
}
}
document.getElementById('creator').addEventListener('click', add_element, false);
window.addEventListener('DOMNodeInserted', element_created, false);
</script>
</body>
</html>
in the hope that the events would be the same, or that upon inspecting the DOMNodeInserted event, I'd find some way of extracting the original click event from the insertion event's properties. I found nothing within the properties of the insertion event that linked it to the click event, though.
Is there any way of finding which event triggered a DOMNodeInserted event using JavaScript?
Use custom event when you are inserting elements from javascript, but only based on event "DOMNodeInserted", it impossible, still you can use a custom attribute on element <div insertedwith="click"> and check on "DOMNodeInserted" handler if e.target has that attribute. Anyway check the example, hope will help you.
console.clear();
function add_element_working(e)
{
var div = document.createElement('div');
div.onload = function() {
console.log("Script loaded and ready");
};
div.innerHTML = "This is tracked with custom event";
event = document.createEvent("HTMLEvents");
event.initEvent("InsertedFromClick", true, true);
document.body.appendChild(div);
div.dispatchEvent(event);
}
function add_element_notworking(e)
{
var div = document.createElement('div');
div.innerHTML = "This is not tracked";
document.body.appendChild(div);
}
function element_created(e)
{
console.log("Inserted element not knowing who trigered");
console.log(e);
}
function element_created_custom(e)
{
console.log("Click inserted element traked");
console.log(e);
}
document.getElementById('creator').addEventListener('click', add_element_working, false);
document.getElementById('creator2').addEventListener('click', add_element_notworking, false);
window.addEventListener('DOMNodeInserted', element_created, false);
window.addEventListener('InsertedFromClick', element_created_custom, false);
<div id="creator">Click me to check that tracked</div>
<div id="creator2">Click me to check that I'm not tracked</div>
<div>HTML</div>

redips.drag get row id when row is dropped and send to server

I am building a django site and have implemented the redips.drag library in one of my pages to allow dragging of table rows. I want a very simple functionality in my code- add a listener, so when the row is dropped, it send the row data to the server. jQuery-speaking, something like this:
$(function() {
$(someDomElement).on('DropEvent', function() {
// send data to server
};
});
The problem though, is that redips.drag is not a jQuery plugin but a javascript one, so my knowledge is a little (more than a little) lacking. I can probably find some other library, but it's performing really well and I prefer understanding how to work with it than look for a different one.
I can probably handle the "sending the data to the server" part by myself, what I can't understand at all is how to "catch" the drop event, what part of the dom do I listen to? I tried adding monitorEvents to different selectors but failed completely.
I also tried to manipulate the script.js file (the one that initializes the row handling), but also failed. here's the one I'm using (example 20 in the redips package):
"use strict";
// define redips object container
var redips = {};
redips.init = function () {
// reference to the REDIPS.drag library and message line
var rd = REDIPS.drag,
msg = document.getElementById('msg');
// initialization
rd.init();
//
// ... more irrelevent code ...
//
// row event handlers
//
// row clicked (display message and set hover color for "row" mode)
rd.event.rowClicked = function () {
msg.innerHTML = 'Clicked';
};
// row row_dropped
rd.event.rowDropped = function () {
msg.innerHTML = 'Dropped';
};
// and so on...
};
// function sets drop_option parameter defined at the top
redips.setRowMode = function (radioButton) {
REDIPS.drag.rowDropMode = radioButton.value;
};
// add onload event listener
if (window.addEventListener) {
window.addEventListener('load', redips.init, false);
}
else if (window.attachEvent) {
window.attachEvent('onload', redips.init);
}
Now I tried adding a console.log('hello') to the rd.event.rowDropped function (right above the msg.innerHTML line), but that doesn't work, I drop the row and nothing shows in the log. Doing a console.log outside the init function works so I know the script can pass stuff to the console.
Please, can anyone help me? I'm at a complete loss...
I know this may be a little lateto answer your question but I found the answer. You need to use the event dropped and the attribute rd.obj (REDIPS.drag.obj) to get the id use it with simple javascript like getAttribute('id')
redips.init = function () {
// reference to the REDIPS.drag library and message line
var rd = REDIPS.drag,
msg = document.getElementById('msg');
// initialization
rd.init();
// row clicked (display message and set hover color for "row" mode)
rd.event.clicked = function () {
msg.innerHTML = 'Clicked' + rd.obj.getAttribute('id');
};
// row row_dropped
rd.event.dropped = function () {
msg.innerHTML = 'Dropped' + rd.obj.getAttribute('id');
};
};

Categories

Resources