I have an html template that i'm using template literals for. The function looks like the below
// postCreator.js
export const blogPostMaker = ({ title, content, address, id }, deletePost) => {
const innerHtml = `
<blog-post>
<h1 class='title'>${title}</h1>
<p class='content'>${content}</p>
<p class='address'>${address}</p>
<button onclick='${() => deletePost(id)}'>Delete</button>
</blog-post>
`
return innerHtml
}
//Blog.js
postHelper.getSeriesOfPosts(10)
.then(resp => {
resp.forEach(post => (blogDiv.innerHTML += blogPostMaker(post, postHelper.deletePost)))
})
What I can't figure out is how to get the onclick to work. I've tried passing in an anon function in Blog.js
to the postCreator
as well with no luck.
Any ideas?
If you don't want to expose the event callback globally, you should attach it in the JS part of the code with addEventListener()
:
// postCreator.js
export const blogPostMaker = ({ title, content, address, id }) =>
`
<blog-post id='${id}'>
<h1 class='title'>${title}</h1>
<p class='content'>${content}</p>
<p class='address'>${address}</p>
<button>Delete</button>
</blog-post>
`
//Blog.js
postHelper.getSeriesOfPosts(10).then(resp => {
resp.forEach(post => {
blogDiv.innerHTML += blogPostMaker(post)
blogDiv.querySelector(`blog-post[id="${post.id}"] > button`)
.addEventListener('click', () => postHelper.deletePost(post.id))
})
Note: it's not the most efficient way to do it but it keeps your file structure.
Instead I would create the <button>
directly with createElement()
and then add it to DOM with appendChild()
, or I would use a DocumentFragment or a <template>
in order to avoid querySelector()
on all of the blogDiv
.
If you absolutely want to use inline JS without exposing the helper you'll need to define your as a component (for example a Custom Element).
const markUp = `
<button onclick="myFunction()">Click me</button>
`;
document.body.innerHTML = markUp;
window.myFunction = () => {
console.log('Button clicked');
};
Miroslav Savovski's solution works but they did not explain why, so I thought I would add this answer with the reasoning behind that and a step-by-step of how it is actually working, and why the OP's solution was not working initially.
TLDR? Scroll to the last two code snippets.
With template literals when you put a function inside of them it executes that function, so let's say we have a simple function like this that just returns a string of 'blue'
:
const getBlueColor = () => 'blue'
And then we call this function inside of a template literal like this:
<div>${getBlueColor()}</div>
What happens is that the getBlueColor()
is called right when that code is executed.
Now lets say we wanted to do this onclick
instead like this:
<div onclick="${getBlueColor()}"></div>
What is happening here is that getBlueColor
is not executed onclick
, it's actually executed whenever this template literal is executed.
The way we fix this is to prevent the template literal from executing this function by simply removing the template literal:
<div onclick="getBlueColor()"></div>
But now let's say you want to pass in some parameters to a function like getOppositeColor(color)
like this:
<div onclick="getOppositeColor(color)"><div>
This will not work because color
won't be defined. So you need to wrap that with a template literal and in a string like this:
<div onclick="getOppositeColor('${color}')"><div>
Now with this you will be calling the onclick
when the user clicks the button, and you will be passing it a string of the color
like this:
getOppositeColor('blue')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With