Drop #727 (2025-11-06): Forgotten But Not Gone

Cellular Macinations; You Don’t Have To Go Home But You Can’t Stay Here; It’s All About That <base>

Every day, we’re pressured to always consider the “new”, so we’re going to slow the pace down a bit today and focus on three simple items that harken back to simpler, leaner times.


TL;DR

(This is an LLM/GPT-generated summary of today’s Drop. This week, I continue to play with Ollama’s “cloud” models for fun and for $WORK (free tier, so far), and gave gpt-oss:120b-cloud a go with the Zed task. Even with shunting context to the cloud and back, the response was almost instantaneous. They claim not to keep logs, context, or answers, but I need to dig into that a bit more.)


Cellular Macinations

I make plenty of charts every single day to try to get a handle on what’s going on in the big, bad internets. Of late, I also have need to pair some of these visuals with the underlying data tables in some new dashboards, and it’s getting increasingly difficult (at least at work) to justify relying on third-party libraries like DataTables, AG Grid, or Tanstack Table to help with this, given the massive uptick in JS supply chain shenanigans happening this year. (Sadly, my fav site framework, Web Awesome, has yet to release their “data grid” component).

Weirdly coincidentally enough, I was recently reminded of the HTMLTableElement API, a built-in browser JS interface (dating back to the DOM Level 1 standard from 1998!) specifically designed for creating and manipulating HTML tables. Unlike generic DOM manipulation, it understands table structure (i.e., rows, cells, headers, and footers) which makes building tables cleaner with semantic and intuitive methods.

It’s super clean:

const books = [
  { title: 'Leviathan Wakes', year: 2011, pov: 'Holden & Miller' },
  { title: 'Caliban\'s War', year: 2012, pov: 'Holden, Bobbie, Prax & Avasarala' },
  { title: 'Abaddon\'s Gate', year: 2013, pov: 'Holden, Bull, Anna & Melba' },
  { title: 'Cibola Burn', year: 2014, pov: 'Holden, Elvi, Havelock & Basia' },
  { title: 'Nemesis Games', year: 2015, pov: 'Holden, Naomi, Alex & Amos' },
  { title: 'Babylon\'s Ashes', year: 2016, pov: 'Multiple POVs' },
  { title: 'Persepolis Rising', year: 2017, pov: 'Holden, Bobbie & others' },
  { title: 'Tiamat\'s Wrath', year: 2019, pov: 'Holden, Elvi, Naomi & Teresa' },
  { title: 'Leviathan Falls', year: 2021, pov: 'Multiple POVs' }
];

const table = document.createElement('table');

// Create header
const headerRow = table.insertRow();
['Title', 'Year', 'Point of View Characters'].forEach(heading => {
  const cell = headerRow.insertCell();
  cell.textContent = heading;
});

// Create data rows
books.forEach(book => {
  const row = table.insertRow();
  row.insertCell().textContent = book.title;
  row.insertCell().textContent = book.year;
  row.insertCell().textContent = book.pov;
});

table.style.borderCollapse = 'collapse';
table.style.width = '100%';

document.body.appendChild(table);

(You can see that in action with some other cellular machinations in this live example.)

There is real benefit to this approach. Because the DOM API is built into every browser, you can work with it without pulling in any npm packages, so there’s no extra bundle weight and no risk of version clashes. The methods are ready the instant the DOM loads, so you don’t have to wait for a library to initialise or parse anything. For straightforward tables that only need to show data, the native API beats a full‑featured grid library by sidestepping the extra abstraction layers that add latency. Using the proper DOM methods instead of building HTML strings with .innerHTML also shields you from XSS attacks. Finally, because you’re relying on native browser features, you avoid the surprise deprecations or interface changes that can suddenly break third‑party scripts.

If you simply need to render diminutive data, the built‑in methods are usually sufficient. Once a table starts to demand more than just static rendering, a third‑party grid becomes worthwhile. Scenarios such as virtual scrolling for tens of thousands of rows, advanced sorting, filtering, or pagination, interactive column resizing/reordering, in‑cell editing with validation, or exporting to CSV/Excel/PDF all require the richer feature set that a dedicated library provides. Likewise, if you need a table that radically adapts to different screen sizes with responsive transformations, a purpose‑built solution will likely save you a lot of hand‑crafted code.

A couple of quirks do show up when you work directly with the table API. First, insertCell() always produces a <td> (it cannot create a <th>), so if you need proper header cells you have to build them yourself with document.createElement('th'). And, you oddly need to use a -1 index for appending to a table. But you could abstract these away with a few lines in your own, small, utility library.

So, the DOM tables API is far from abandonware. I think it’s a perfectly viable tool that’s been overshadowed by the JavaScript ecosystem’s preference for libraries. For many projects, especially those prioritizing performance, simplicity, and minimal dependencies, this native API deserves a second look.


You Don’t Have To Go Home But You Can’t Stay Here

Every once in a while, you stumble across a command that feels less like software and more like a pleasant reminder from another, simpler time. leave is one of those.

Fire up your terminal and type:

$ leave +1730

and you’ll see something like this later on:

You have to leave in 15 minutes.
You have to leave in 10 minutes.
You have to leave in 5 minutes.
You have to leave NOW!

It’s elegantly simple. You tell your machine when you need to go, and it gently taps you on the shoulder when it’s time. The final message keeps showing up every minute until you log out or cancel (i.e., kill) it.

That’s it. No GUI pop-ups, no daemons, no cron jobs, no mobile notifications. Just a little timekeeper that lives in your shell.

Under the hood, leave is truly simple. It:

  • takes a time (absolute like 1730, or relative like +30 for “30 minutes from now”)
  • calculates the difference between now and then
  • sleeps
  • prints reminders directly to your terminal device (/dev/tty) when the time comes

That’s it!! The source code is succinct and readable, and is ripe for a Rust port.

On BSD systems — macOS, FreeBSD, OpenBSD — it’s usually found in /usr/bin/leave. On Linux, you will most likely need to deliberately install it.


It’s All About That <base>

Photo by vedanti on Pexels.com

(I’m pretty sure that’s the 10,000th time I’ve abused that phrase.)

This is a quick one. <base> is a foundational HTML element, and somewhat recent-ish stats from the Web Almanac show around 10% of catalogued HTML pages use it. The primary purpose of the tag is to set the “base” href for all relative links on a page. I’m not sure that’s super relevant anymore, and it can be a pain if you switch domains/sub-paths.

In modern web development, messing with href can also cause more problems than it solves. If you’re using fragment identifiers for in-page navigation (e.g., #section-2) or JavaScript-driven interactions, setting the href in <base> will hijack those too. Your smooth-scrolling anchor links? Broken. Your tab components using #tab-1? Probably broken. Single-page apps (SPAs)? LOL!

It is super useful to help avoid manually setting the target attribute, or using your own or third-party JS after a page loads to do so. It won’t break SPAs the way href does, since it only affects where links open (same tab, new tab, specific frame, etc.). Your SPA’s router is still intercepting link clicks and handling navigation through  event.preventDefault() on click events as the target attribute doesn’t change that.

So, save some keystrokes and JS clock cycles and see if we can bump up <base> usage stats for some future Web Almanac report!


FIN

Remember, you can follow and interact with the full text of The Daily Drop’s free posts on:

  • 🐘 Mastodon via @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev
  • 🦋 Bluesky via https://bsky.app/profile/dailydrop.hrbrmstr.dev.web.brid.gy

☮️

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.