why html`<button onclick="displayDate()">The time is?</button>` not work?

I tried to implement the code example below inside a notebook here


<body>

<p>Click the button to display the date.</p>

<button onclick="displayDate()">The time is?</button>

<script>
function displayDate() {
  document.getElementById("demo").innerHTML = Date();
}
</script>

<p id="demo"></p>

</body>

However, it seems using a function with html and <button onclick=""> is not straightforward here.

html`<button onclick="displayDate()">The time is?</button>`  // cell 1

I first tried cell 1 which uses a function (displayDate) inside quotation, nothing works.

html`<button onclick="${displayDate()}">The time is?</button>` // cell 2

Then I tried cell 2. This time the function can be executed, but clicking the button won’t actually execute the function.

I know there are other ways to click the button to execute the function, I just wonder:

  1. why exactly cell 1 won’t work
  2. why clicking the button based on cell 2 won’t execute the function
  3. are there easy solutions to make cell 1 and cell 2 work?

Thanks!

1 Like

Here’s the last line of your notebook:

import {webpage, html} from "@embracelife/tutorial-utilities"

When you import html, you overwrite the built in html template function. If you comment that line out, the code works as expected.

As you say, though, “there are other ways to click the button to execute the function”. Indeed, the button demo in the Inputs notebook listed in the User Manual illustrates exactly this task.

Thanks for your reply, but the import cell has nothing to do with the problem I posted above. Maybe I didn’t describe the problem I have clearly.

So now I have tried to make the notebook consistent with my question here by adding cell 1 and cell 2 comment in the notebook. please see the notebook w3school's JS Events tutorial / 深度碎片 | Observable

The questions I have:

  1. why neither running cell 1 and clicking the button produced by cell 1 can display the date?
  2. why clicking the button produced by cell 2 won’t display the date? ( I understand that running cell 2 can display the date)

Thanks

Cell 1 doesn’t work because when you embed JavaScript into the onclick attribute, it runs in the global context. Named cells in Observable aren’t globals—they are only accessible to other cells in the notebook—and so assuming that displayDate is a cell in your notebook, it’s not a global.

Cell 2 doesn’t work because you’re immediately invoking the displayDate function as part of the expression, and then interpolating the returned value into the HTML. Since displayDate returns undefined, the result is:

html`<button onclick="undefined">The time is?</button>`

Here are two ways to add an event listener to a DOM element. First, you can use Object.assign to set the onclick property of the element:

Object.assign(html`<button>The time is?</button>`, {onclick: displayDate})

Second, you can explicitly call addEventListener:

{
  const button = html`<button>The time is?</button>`;
  button.addEventListener("click", displayDate);
  return button;
}

By coincidence, I am working on a new tagged template literal of Observable which will allow you to assign event listeners inside embedded expressions. When this is released, you will be able to say simply:

html`<button onclick=${displayDate}>The time is?</button>`

I’m giving a preview of this work at tonight’s notebook and will share the notebook soon.

One other note: selecting global elements from the DOM is an antipattern in Observable because cells run in topological order. You can fix this by giving your demo cell a name instead of an id attribute:

demo = html`<p></p>`
function displayDate() {
  return demo.innerHTML = Date();
}

Or consider using a view. For example, if you want a button that causes another cell to run:

viewof update = html`<button>Update the time`
date = {
  update; // Referencing update causes this cell to run when viewof update is clicked.
  return new Date;
}
5 Likes

Here’s the notebook and new tagged template literal I mentioned:

1 Like

Thanks a lot @mbostock ! Your replies are amazing, I learnt a lot from these. :heart: :+1:

2 Likes