JavaScript: Detecting the end of native smooth scrolling

When you use native smooth scrolling there is no built-in method to detect the end of the scrolling animation. Methods like scrollTo() don't return a promise. We will eventually get a scrollend event, but that is still some time away.

Until then I'm using the following `awaitScrollE...

Rails: Assigning associations via HTML forms

Let's say we have posts with an attribute title that is mandatory.

Our example feature request is to tag these posts with a limited number of tags. The following chapters explain different approaches in Rails, how you can assign such an association via HTML forms. In most cases you want to use Option 4 with assignable values.

The basic setup for all options looks like this:

config/routes.rb

Rails.application.routes.draw do
  root "posts#index"
  resources :posts, except: [:show, :destroy]
end

**db/migrate/...

HTTP headers can only transport US-ASCII characters safely

HTTP header values must only contain low-ASCII (7-bit) characters for safe transport. From RFC 7230:

Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets.

If you need to transport 8-bit+ characters (e.g...

How to read the current breakpoint tier(s) in JavaScript

To read the current breakpoint tier in JavaScript, employ this CSS:

:root {
  --current-breakpoint-tier: xs;
  
  @media (min-width: $screen-sm-min) {
    --current-breakpoint-tier: sm;
  }
  @media (min-width: $screen-md-min) {
    --current-breakpoint-tier: md;
  }
  @media (min-width: $screen-lg-min) {
    --current-breakpoint-tier: lg;
  }
  @media (min-width: $screen-xl-min) {
    --current-breakpoint-tier: xl;
  }
  @media (min-width: $screen-xxl-min) {
    --current-breakpoint-tier: xxl;
  }
}

Then use this JavaScript:

Jasmine: Mocking ESM imports

In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.

Example

We are going to use the same example to demonstrate the different approaches of mocking an imported function.

We have a module 'lib' that exports a function hello():

// lib.js

function hello() {
  console.log("hi world")
}

export hello

We have a second module 'client' that exports a function helloTwice(). All this does is call hello() ...

Bookmarklet: cards Markup Link Bookmarklet

The cards editor has a feature "Cite other card" to create links to other cards in the same deck as mardown links.
If you want to reference a card from a different deck, this bookmarklet might be useful:

javascript:(function () {
  const doAlert = () => { alert("Maybe not a makandra card?") };
  let cardsPathPattern = /(\/[\w-]+\/\d+)-.+/;
  if (window.location.pathname.match(cardsPathPattern)) {
    let currentPath = window.location.pathname.match(cardsPathPattern)[1];
    let title = document.querySelector('h1.note--title')?.textCon...

Using path aliases in esbuild

In esbuild, you usually import other files using relative paths:

import './some-related-module'
import `../../utils/some-utility-module`
import `../../../css/some-css.sass`

This is totally fine if you import closely related files, but a bit clunky when you're trying to import some "global" module, like a utility module. When moving a file, your imports also need to change.

To get around this, esbuild support a mechanism first introduced in TypeScript called "path aliases". It works like this:

First, you create a file called `js...

Jasmine: Creating DOM elements efficiently

Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.

Let's say I need this HTML structure:

<ul type="square">
  <li>item 1</li>
  <li>item 2</li>
</ul>

This card compares various approaches to fabricating DOM elements for testing.

Constructing individual elements

While you can use standard DOM functions to individually create and append elements, this is extremely verbose:

let list = document.createElement('...

Sass: get rid of deprecation warnings in dependencies

TLDR: sass >= 1.35.0 has the option quietDeps to silence deprecation warnings from dependencies.

quietDeps: If true, the compiler must not print deprecation warnings
for stylesheets that are transitively loaded through an import path or importer.


You might have seen deprecation warnings like this during assets compilation:

DEPRECATION WARNING: Using / for division is deprecated and will be removed in Dart Sass 2.0.0.
Recommendation: math.div($grid-gutter-width, 2)
More info and automated migrator: https://sass-la...

Chrome DevTools: DOM Breakpoints - Breakpoints on HTML Elements

tl;dr

In Chrome DevTools in the Elements tab or in Firefox in the Inspector tab you can right click on an element and choose Break on to debug changes related to this element.

Example

DOM Breakpoints can be quite useful to quickly find the JavaScript that is responsible for some (unexpected) behavior. You can use DOM Breakpoints for debugging subtree modifications, attribute modifications or node removal.

Here you can see a very simple example that shows what JavaScript lines are responsible for ...

JavaScript: Testing whether the browser is online or offline

You can use the code below to check whether the browser can make connections to the current site:

await isOnline() // resolves to true or false

Limitations of navigator.onLine

While you can use the built-in function navigator.onLine (sic), it is only a hint for whether the device can access the Internet.

When navigator.onLine === false you know for certain that the user device has no connection to the Internet. This mea...

Show a JS fiddle in fullscreen

If you have a JS fiddle, you can open it in fullscreen by appending /show to the URL.

Example: https://jsfiddle.net/b275g910/3 => https://jsfiddle.net/b275g910/3/show

Capybara: Most okayest helper to download and inspect files

Testing file download links in an end-to-end test can be painful, especially with Selenium.

The attached download_helpers.rb provides a download_link method for your Capybara tests. It returns a hash describing the download's response:

details = download_link('Download report')
details[:disposition]  # => 'attachment' or 'inline'
details[:filename]     # => 'report.txt'
details[:text]         # => file content as string
details[:content_type] # => 'text/plain'

Features

Compared to [other approaches](...

JavaScript: New Features in ES2021

tl;dr

With ES2021 you now can use str.replaceAll(), Promise.any(), logical assignment operators, numeric separators and WeakRef on all major browsers except IE11.

replaceAll

JavaScript's replace(searchValue, replaceValueOrFn) by default replaces only the first match of a given String or RegExp.
When supplying a RegExp as the searchValue argument, you can specify the g ("global") modifier, but you have to remember doing that, hence using replace when you expect global replacement is prone to errors.
When supplying st...

JavaScript: Working with Query Parameters

tl;dr: Use the URLSearchParams API to make your live easier if you want to get or manipulate query parameters (URL parameters).

URLSearchParams API

The URLSearchParams API is supported in all major browsers except IE 11.

It offers you a bunch of useful methods:

  • URLSearchParams.append() - appends a query parameter
  • URLSearchParams.delete() - deletes the specified query parameter
  • URLSearchParams.get() - returns the value of the specified query parameter
  • `URLSearchP...

Jasmine: Fixing common errors during initialization

Due to the way we setup Jasmine tests in our projects, you may run into various errors when Jasmine boots.

Setting jasmineRequire on undefined

Jasmine 4 may fail with an error like this:

Uncaught TypeError: Cannot set properties of undefined (setting 'jasmineRequire')

This is due to issues in Jasmine's [environment detection](https://github.com/jasmine/jasmine/blob/502cb24bb89212917a3c943b593fd918ffc481cb/lib/jasmine-core/...

How to access Chrome Devtools when running JavaScript tests via CLI

While we are used to run our JavaScript tests on a test page within our Browser, it's also possible to run them on the command line with NodeJS. I think that's actually the most common way to run JS tests.

Given a Vue project that uses Jest (via vue-cli-service) with the following package.json:

{
  "scripts": {
    "test": "vue-cli-service test:unit --testMatch='**/tests/**/*.test.js' --watch"
  },
}

This allows us to run J...

Integrating ESLint

Introduction

To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.

1. Adding the gem to an existing code base

You can add the following lines to your package.json under devDependencies:

  "devDependencies": {
    "eslint": "^8.7.0",
    "eslint-config-standard": "^16.0.3",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-node"...

How to add esbuild to the rails asset pipeline

This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.

Preparations

  1. update Sprockets to version 4
  2. add a .nvmrc with your preferred node version (and install it)
  3. add gems jsbundling-rails and foreman to your Gemfile:
    gem 'jsbundling-rails'
    group :development, :test do
      gem 'foreman'
      # ...
    end
    
  4. bundle install
  5. run bin/rails javascript:install:esbuild in a console to prepare esbuild.
  6. run yarn install...

esbuild: Make your Rails application show build errors

Building application assets with esbuild is the new way to do it, and it's great, especially in combination with Sprockets (or Propshaft on Rails 7).
You might be missing some convenience features, though.

Here we cover one specific issue:
Once you have started your development Rails server and esbuild with the --watch option (if you used jsbundling-rails to set up, you probably use bin/dev), esbuild will recompile your assets upon change, but build errors will only be printed to the terminal. Your application won't complain about them ...

Selector for finding the currently selected option in a <select> tag

Use option:checked to find the currently selected option:

select.querySelector('option:checked')

Yes, :checked, not :selected.

This is the same as finding an option with the { selected: true } property in JavaScript:

select.querySelectorAll('option').find((option) => option.selected)

What about the selected attribute?

Note that option[selected] would only find an <option selected> tag. This may be the selected option right after loading the page, but not once the user switched to a different value. ...

The TCF 2.0 (Tranparency and Consent Framework) standard, and what you should know about it

The Interactive Advertising Bureau (IAB) is a European marketing association which has introduced a standard how advertising can be served to users in line with the General Data Protection Regulation (GDPR). This standard is called the TCF 2.0 (Transparency and Consent Framework). If you want to integrate any kind of advertising into a website, chances are the advertising network will require your website to implement that standard. This is a very brief overview of what this means:

The basic idea in the TCF 2.0 ...