I have something that I'm really close to being able to do, but I feel like I'm missing something pretty obvious. My question is how to associate a function to an onclick html attribute in a function that renders some HTML. I'm doing this for learning and illustration, so I'm aware of how this is done pretty straightforward in something like React.
function article_list(article_list_id) {
const onclick_handler = (event) => {
event.preventDefault();
console.log(`I was clicked!!!`)
return false;
}
const render = () => {
console.log("I was rendered!!!")
articleElement = document.getElementById(article_list_id)
articleElement.innerHTML = `
<ul>
${some_array_of_items.map(item => {
return `
<li class="text-sm flex items-center">
<span><a href="#" class="list-checklist" data-id="${item.id}" onclick="${onclick_handler}">${item.title}</a></span>
</li>`
}).join('')}
`
}
return Object.assign({}, {
render,
})
Here, I'd like for the a link to be able to have a simple onclick=""
html attribute that then calls the function. I know that I am messing up my .bind()
or .apply()
and, because I am appending a string to the .innerHTML
value, I'm not fully sure if there is an impact there because of it.
I know there is an answer, but thought I would ask here in hopes of getting pointed in the right direction. Again, I know that I could React to do this, but the goal of my exercise is to learn / understand how to do this w/out React. Thank you!
Instead of inserting the HTML all at once, insert a <li>
element on each iteration (without changing the HTML inside the same container, so as not to corrupt existing listeners). Once the <li>
is inserted, select the <a>
and add a listener to it with addEventListener
.
const render = () => {
const parent = document.getElementById(article_list_id);
parent.textContent = '';
const ul = parent.appendChild(document.createElement('li'));
for (const item of some_array_of_items) {
const li = ul.appendChild(document.createElement('li'));
li.className = "text-sm flex items-center";
li.innerHTML = `<span><a href="#" class="list-checklist" data-id="${item.id}">${item.title}</a></span>`;
li.querySelector('a').addEventListener('click', onclick_handler);
}
}
If the input happens to be untrustworthy, this can result in arbitrary code execution, which is not desirable. In such a case, don't interpolate into the HTML, set the properties and contents after inserting the LI.
const li = ul.appendChild(document.createElement('li'));
li.className = "text-sm flex items-center";
li.innerHTML = `<span><a href="#" class="list-checklist"></a></span>`;
const a = li.querySelector('a');
a.dataset.id = item.id;
a.textContent = item.title;