Thursday, May 23, 2024
 Popular · Latest · Hot · Upcoming
123
rated 0 times [  128] [ 5]  / answers: 1 / hits: 5443  / 5 Years ago, sun, september 1, 2019, 12:00:00

I would like to attach an event listener (that calls a function) to a button that is not yet present in the DOM. I don't want to use inline-call in the HTML how can I do it?



So far i could achieve what I wanted by creating a global event listener that checks if the element clicked has a specific Id. But I found this solution to be dirty and not optimal.



const messageForm = document.querySelector(#message-form)
const messageTextarea = document.querySelector(#message-textarea);
const messageList = document.querySelector(#message-list);
const messageEmpty = document.querySelector(#message-empty);
let messageNumber = 0;

const messageFormatted = messageText => {
return `
<li class=message-item>
<img class=message-avatar src=./icons/boy.png alt=Username>
<article class=message-content>
<p class=author>Ernesto Campese</p>
<p class=message>${messageText}</p>
<p class=datetime>A moment ago</p>
</article>
<div class=message-actions>
<img id=message-edit class=action-button src=./icons/edit.png alt= width=22 height=22>
<img id=message-delete class=action-button src=./icons/delete.png alt= width=22 height=22>
</div>
</li>
`;
}

document.addEventListener(click, (e) => {
if (e.target.id === message-delete) {
e.target.parentNode.parentNode.remove();
messageNumber--;
messageEmptyCheck();
}
})

messageForm.addEventListener(submit, (e) => {
e.preventDefault();
if (messageTextarea.value !== ) {
messageList.insertAdjacentHTML(beforeend, messageFormatted(messageTextarea.value));
messageTextarea.value = null;
messageTextarea.focus();
messageNumber++;
messageEmptyCheck();
}
});


As you can see in the code, inside the <li> that I'm creating there are two IMG, one is for deleting and the other for editing. I want to add an event listener to the delete IMG, so when the user clicks it, the li gets eliminated.



The problem is that I cannot create any function if the element does not exist yet. I would love to do something like:



const messageDeleteButton = document.querySelector(#message-delete);

messageDeleteButton.addEventListener(click, (e) => {
e.parentNode.parentNode.remove();
}
})


Hope I was clear enough, thank you guys!


More From » html

 Answers
1

You could parse your HTML string to a document in JS and then add the event listener to that element.



So you've got your Template Literal string.



const messageFormatted = messageText => {
return `
<li class=message-item>
<img class=message-avatar src=./icons/boy.png alt=Username>
<article class=message-content>
<p class=author>Ernesto Campese</p>
<p class=message>${messageText}</p>
<p class=datetime>A moment ago</p>
</article>
<div class=message-actions>
<img id=message-edit class=action-button src=./icons/edit.png alt= width=22 height=22>
<img id=message-delete class=action-button src=./icons/delete.png alt= width=22 height=22>
</div>
</li>
`;
}


You can transform your string into HTML by writing a function like the one below.
It uses the DOMParser API which creates a new document that will contain all of your HTML you've written in your string.



const parseStringToHTML = (str) => {
const parser = new DOMParser();
return parser.parseFromString(str, 'text/html');
};


And this document works like the one you have in your page, its just a new one that only exists in your JS memory for the time being. Give your string as argument to parseStringToHTML to create HTML.



const message = messageFormatted('This is the message'); // Example message
const messageHTML = parseStringToHTML(message); // Returns a document object with all the features a document object has.


So now that your string is a document you can use methods on it like getElementById, querySelector, etc. But first you must select the element that you need.



const messageDeleteButton = messageHTML.querySelector(#message-delete);


See that I've used messageHTML instead of document. In this scenario messageHTML is a document and therefor we can query inside of it. And now you've found your element inside this document you can add an event listener to it.



messageDeleteButton.addEventListener(click, (e) => {
e.parentNode.parentNode.remove();
});


Okay, so now the event listener has been added. All you have to do now is append the HTML you need from the messageHTML into the document of your page. The HTML can be found in the messageHTML.body property.



Now instead of insertAdjacentHTML use insertAdjacentElement to insert the element in the position of your choosing. The element you want to append is the <li class=message-item> which would be the firstElementChild in the body of the messageHTML.body property.



So in your submit event listener of your form change the following line from:



messageList.insertAdjacentHTML(beforeend, messageFormatted(messageTextarea.value));


To all that I've explained above.



// Create string
const message = messageFormatted(messageTextarea.value);

// String to document
const messageHTML = parseStringToHTML(message);

// Select delete element.
const messageDeleteButton = messageHTML.querySelector(#message-delete);

// Add event listener to delete element.
messageDeleteButton.addEventListener(click, (e) => {
e.parentNode.parentNode.remove();
});

// Append the <li> element to the messageList.
messageList.insertAdjacentElement(beforeend, messageHTML.body.firstElementChild);


Don't forget to include the parseStringToHTML function in your code. I understand that this is a lot to work out so if you have any question please do ask.



I hope that this will help you out.



Note I see that event delegation (global event listener) is not something you want to do, although as other have stated, it is more performant and easier to implement than other methods, even my own answer. It would also solve listening for elements that you've added or even removed from your list. You could set it even on the <ul id=message-list> element like T.J. Crowder suggested.


[#6385] Thursday, August 29, 2019, 5 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
tatyanna

Total Points: 552
Total Questions: 96
Total Answers: 96

Location: Cook Islands
Member since Thu, May 21, 2020
4 Years ago
tatyanna questions
Sun, Oct 23, 22, 00:00, 2 Years ago
Tue, Jul 14, 20, 00:00, 4 Years ago
Sun, Nov 17, 19, 00:00, 5 Years ago
Tue, Jan 15, 19, 00:00, 5 Years ago
;