Go back

Changing button content

November 19, 2022

Sometimes you need a button which changes when you click on it. In this post we’ll look at two different ways to approach this. Most of the time, I think that approach 2 is the best choice.

Let’s take a look at an example:

The HTML for this button:

<!-- Yes, we can use emoji as id's! -->
<button id="πŸ˜…">Click me!</button>

The javascript to handle the click:

  const button = document.getElementById("πŸ˜…");

  // I didn't include the `debounce` function,
  // because I want to keep the example simple.
  const updateButtonTextAfterOneSecond = debounce(() => {
    button.textContent = "Click me... again!";
  }, 1000);

  button.addEventListener("click", () => {
    button.textContent = "You did well!";

When you click on the button, the content changes. But, the width of the button changes as well.

If we would like the button to have a fixed width, based on the width of the largest element inside the button, we could do that:


We put all possible contents of the button on top of each other and change the visibility of those children whenever we click on the button.

There are multiple ways to put those children of top of each other, but I like to make the parent (the button in this case) a grid with one row and one column. Then make sure that the children are put inside the same row and column.

So the HTML would be:

<button id="😎">
  <div>Click me!</div>
  <div class="invisible">You did well!</div>
  <div class="invisible">Click me... again!</div>

Note that we already include all possible children inside the HTML.

The second child has the invisible class, which we define ourselves. It only sets visibility to hidden. The CSS for our button is:

  button {
    display: inline-grid;
    grid-template-columns: 1;
    grid-template-rows: 1;

  button > div {
    grid-column: 1;
    grid-row: 1;

  .invisible {
    visibility: hidden;

And finally, the javascript, which add/removes the invisible class from the children of the button:

  const button = document.getElementById("😎");

  const updateButtonContentAfterOneSecond = debounce(() => {
  }, 1000);

  button.addEventListener("click", () => {
    // Actually, accessing the children based on the 
    // index like this is usually something you want to avoid.
    // It can easily lead to bugs in the future. 
    // But #yolo for this example.


This method also works for buttons where you want to replace the contents of the button with a loading spinner, without decreasing the width of the button:

That’s it!