Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting HTML Button`onclick` in template literal

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?

like image 505
Elliott McNary Avatar asked Apr 08 '17 18:04

Elliott McNary


3 Answers

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

like image 138
Supersharp Avatar answered Oct 15 '22 02:10

Supersharp


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

document.body.innerHTML = markUp;

window.myFunction = () => {
    console.log('Button clicked');
};
like image 40
Miroslav Savovski Avatar answered Oct 15 '22 02:10

Miroslav Savovski


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')
like image 22
maxshuty Avatar answered Oct 15 '22 02:10

maxshuty