Best way to call function from html?

hey! in python, there’s a concept of “pythonic code” that adheres to the idiosyncrasies of python. first off, is there such a concept in javascript? the closest i’ve seen is something like the airbnb style guide.

follow up question to that, what is best practice for calling a function on something like a button click in html? there are several ways to do it, but what is considered the cleanest and most readable? i’ve personally been using <button onclick="foo()"> but don’t know if it’s the best option.

thank you for your time ^^

Hi there!

This is a very good question with unfortunately a slightly unsatisfying answer, unless you were to ask this on a very cough opinionated cough platform such as “The Orange Site” or “The Blue Bird’s Site”.

Regarding style

Even though people would like to argue that whatever style they adopted is the best, unfortunately, JavaScript (and the broader ECMAScript) has no official nor defacto standard. I would argue that in recent years the following three are still commonplace:
- The AirBnB style guide which you’ve already linked
- StandardJS
- Whatever Prettier + ESLint : recommends has as default

In the end, these are styles and not standards (despite StandardJS being called Standard). FAANG cannot agree which one to adopt, and thus generally I recommend adding ESLint and Prettier so you don’t need to think about most of it. The recommended set of ESLint rules isn’t that opinionated and majority of people agrees on it.

Regarding button clicks

There is a problem with onClick which, apart from “polluting” the HTML element with string-encoded-JavaScript which needs to be evaluated. You cannot mark attribute-provided scripts as safe using a nonce, which means you cannot adopt CSP if you wanted to.

The second problem with the onclick attribute is that it can hold at most one click listener. In general not an issue, but there are plenty of cases where you want to add more than one event listener to a specific DOM element.

So what do we do?

  • Use element.addEventListener('click', ...)
  • Use something unobtrusive like Stimulus

For example, here is an implementation of that last framework for a print button, which is hidden if the window.print() functionality isn’t available:

import { toggleHidden } from 'src/utils/dom-toggle';
import { Controller } from 'stimulus';

/**
 * Allows a button to open the print dialog, if this feature is enabled.
 *
 * This controller automatically removes the hidden attribute if printing
 * is enabled, and leaves it in place if there is no printing API, or if
 * JavaScript is disabled.
 *
 * You can use the print: modifier to change the CSS on the page when printed.
 *
 * @example The HTML for a minimal example
 *
 * <button
 *   hidden
 *   data-controller="print"
 *   data-action="print#onPrint"
 *   type="button"
 *   class="button --primary print:hidden whitespace-nowrap">
 *   <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 -ml-1 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
 *     <path stroke-linecap="round" stroke-linejoin="round" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
 *   </svg>
 *   <span>
 *     Print
 *   </span>
 * </button>
 */
export default class PrintController extends Controller {
  static targets = ['trigger'];

  private declare triggerTarget: HTMLButtonElement;
  private declare hasTriggerTarget: boolean;

  public connect(): void {
    // Show element if printing is possible
    if (window && 'print' in window && typeof window.print === 'function') {
      toggleHidden(this.element, false);

      if (this.hasTriggerTarget) {
        toggleHidden(this.triggerTarget, false);
      }
    }
  }

  public disconnect(): void {
    toggleHidden(this.element, true);

    if (this.hasTriggerTarget) {
      toggleHidden(this.triggerTarget, true);
    }
  }

  public onPrint(): void {
    window.print();
  }
}

You would use this by adding the following HTML:

<button
  hidden
  data-controller="print"
  data-action="print#onPrint"
  type="button"
  class="button --primary print:hidden whitespace-nowrap">
  
  <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 -ml-1 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
    <path stroke-linecap="round" stroke-linejoin="round" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
  </svg>
  <span>
    Print
  </span>

</button>

This button will show “itself” when the JavaScript loads, but only if the print function is available. In my opinion, this is a very good way to write JavaScript, in general.

Conclusion

In general, I would ignore anyone who says “this is the number 1 way to do x” because the beauty of this language is that there are always 10 equally shitty ways to do it. I merely recommend you pick a style, and stick to it. The AirBnB guide is pretty strict (and overwhelming, and in my opinion not always right), so I would go for a looser approach, like prettier + eslint, but YMMV.

3 Likes

this is an amazing response! thank you so much for your insight :D

Thanks for the pointer to Stimulus. A very different thought process indeed…