9

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?

Supersharp
  • 29,002
  • 9
  • 92
  • 134
Elliott McNary
  • 1,149
  • 3
  • 11
  • 20
  • have you tried `` ? – Supersharp Apr 12 '17 at 12:17
  • That would work if I exposed the `postHelper` object globally, but I don't think I want to do that. I don't think what I'm trying to do is possible unless I expose globals based on my reading of how HTML handles inline JS. – Elliott McNary Apr 13 '17 at 20:39

3 Answers3

5

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).

labarna
  • 668
  • 1
  • 9
  • 16
Supersharp
  • 29,002
  • 9
  • 92
  • 134
5

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:

`The color is ${getBlueColor()}`

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:

<button onclick="${getBlueColor()}"></button>

What is happening is that getBlueColor is not being executed onclick, it's actually executed when the 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:

<button onclick="getBlueColor()"></button>

But now let's say you want to pass in some parameters to a function like getOppositeColor(color) like this:

<button onclick="getOppositeColor(color)"></button>

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:

<button onclick="getOppositeColor('${color}')"><button>

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')
maxshuty
  • 9,708
  • 13
  • 64
  • 77
4

const markUp = `
    <button onclick="myFunction()">Click me</button>
`;

document.body.innerHTML = markUp;

window.myFunction = () => {
    console.log('Button clicked');
};
Miroslav Savovski
  • 2,300
  • 2
  • 10
  • 12