---
title: "Today I learned · Ihab Khattab"
description: "Short notes by Ihab Khattab on things he learns along the way: Git, JavaScript, CSS, and the web."
url: "https://ihabkhattab.com/til/"
---

# Today I learned

Short notes on things I pick up along the way. Many started as posts on X.

## [WPO Stats: performance wins with business numbers](https://ihabkhattab.com/til/wpo-stats/)

Oct 11, 2026

[WPO Stats](https://wpostats.com/) collects case studies where faster sites moved conversion, bounce rate, revenue, and the like—not just Core Web Vitals. Handy when you need a real quote instead of “speed matters.”

## [Clean up merged branches with git branch --delete-merged](https://ihabkhattab.com/til/git-branch-delete-merged/)

Oct 2, 2026 · [Also on X](https://x.com/HO_BA/status/2105779699519569932)

Git 2.56 adds `git branch --delete-merged`, so you can stop using this:

```sh
git branch --merged | grep -v "^\*\|main\|master" | xargs git branch -d
```

It deletes local branches whose work has already landed on the upstream they track:

```sh
git branch --dry-run --delete-merged origin  # preview
git branch --delete-merged origin
```

It’s also safer than the one-liner: it skips branches checked out in any worktree and any branch with `branch.<name>.deleteMerged` set to `false`. [Docs](https://git-scm.com/docs/git-branch)

## [CSS content accepts alternative text](https://ihabkhattab.com/til/css-content-alt-text/)

May 19, 2026 · [Also on X](https://x.com/HO_BA/status/2056616160716612033)

The CSS `content` property accepts alternative text after a slash, so generated content in `::before` and `::after` no longer has to hurt screen reader users.

```css
.new-item::before {
  /* "black star" and element content is read out */
  content: "★";

  /* "Highlighted item" and element content is read out */
  content: "★" / "Highlighted item";

  /* Generated content is ignored and only element content is read out */
  content: "★" / "";
}
```

Supported in every major engine since Firefox 128. Via [Stefan Judis](https://www.stefanjudis.com/today-i-learned/css-content-accepts-alternative-text/).

## [Copy changes without the commit: git cherry-pick --no-commit](https://ihabkhattab.com/til/git-cherry-pick-no-commit/)

Nov 4, 2024 · [Also on X](https://x.com/HO_BA/status/1853516079126966548)

`--no-commit` applies a commit’s changes to your working tree and index without creating a commit. Super handy when I want the changes from another branch but not the commit itself.

```sh
git cherry-pick <commit-hash> --no-commit
```

The short form is `-n`, and it works with ranges too: `git cherry-pick -n A..B` stages the combined changes so you can commit them as one.

## [Chain errors with the cause option](https://ihabkhattab.com/til/error-cause/)

Jul 24, 2024 · [Also on X](https://x.com/HO_BA/status/1816073285290828047)

When throwing a new error, pass the original one as `cause`. Debugging gets easier because the original error and its stack trace are kept.

```js
try {
  connectToDatabase();
} catch (err) {
  throw new Error("Connecting to database failed.", { cause: err });
}
```

This way, the logs show both what failed and why. [MDN: Error.cause](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause)

## [JSON Feed: RSS, but JSON](https://ihabkhattab.com/til/json-feed/)

Nov 24, 2023 · [Also on X](https://x.com/HO_BA/status/1728085482272100830)

[JSON Feed](https://www.jsonfeed.org/) is a feed format like RSS and Atom, written in JSON instead of XML. I stumbled upon it while playing with a project that required converting RSS to JSON.

```json
{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "My Example Feed",
  "home_page_url": "https://example.org/",
  "feed_url": "https://example.org/feed.json",
  "items": [
    {
      "id": "2",
      "content_text": "This is a second item.",
      "url": "https://example.org/second-item"
    }
  ]
}
```

Why are we still using XML for feeds when JSON seems to be a more direct, streamlined option?

## [Validate URLs with the URL constructor](https://ihabkhattab.com/til/validate-url-with-url-constructor/)

Aug 22, 2022 · [Also on X](https://x.com/HO_BA/status/1561733178368335877)

A nice way to validate a URL without a regex: let `new URL()` try to parse it.

```ts
/**
 * Check if supplied href is a valid URL or not.
 * Note "example.com/contact" is not a valid URL.
 * The protocol has to be supplied as well:
 * => "https://example.com/contact"
 * => "mailto:iron.man@gmail.com"
 * => "tel:+188899956754"
 */
function isUrl(href: string) {
  try {
    new URL(href);
    return true;
  } catch (e) {
    return false;
  }
}
```

Via [André Landgraf’s remix-isomorphic-link](https://github.com/andrelandgraf/remix-isomorphic-link/blob/main/src/index.tsx#L48-L63).

Update: browsers now ship this built in. `URL.canParse(href)` returns a boolean, no try/catch needed.

## [Check out several branches at once with git worktree](https://ihabkhattab.com/til/git-worktree/)

Oct 30, 2021 · [Also on X](https://x.com/HO_BA/status/1454511806781722632)

`git worktree` checks out another branch into a separate folder, backed by the same repository. No more stashing half-done work just to review a PR or fix something on `main`.

```sh
git worktree add ../hotfix main
cd ../hotfix
# fix, commit, push
git worktree remove ../hotfix
```

I’m in good company: even Guido van Rossum asked why he hadn’t known about it sooner. [Docs](https://git-scm.com/docs/git-worktree) · [Video](https://youtu.be/2uEqYw-N8uE)

## [The ping attribute sends a POST when a link is clicked](https://ihabkhattab.com/til/anchor-ping-attribute/)

Jul 16, 2020 · [Also on X](https://x.com/HO_BA/status/1283538922098827265)

The `ping` attribute on `<a>` makes the browser send an asynchronous `POST` request to a URL when the link is clicked. You might need this for tracking clicks without redirecting through your own server first. Google uses it in search results, with a workaround for Firefox, which disables `ping` by default.

```html
<!-- Sends an asynchronous POST request to /track -->
<a href="https://www.reddit.com/" ping="/track">reddit</a>

<!-- Ping several URLs, separated by spaces -->
<a href="https://www.reddit.com/" ping="/track https://tracking.example/">reddit</a>
```

The request body is just `PING` with `Content-Type: text/ping`, and the `Ping-From` and `Ping-To` headers tell the server which page the click came from and where it went.

For tracking things other than link clicks, the [Beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API) (`navigator.sendBeacon()`) sends a request that survives the page unloading. It was still experimental when I tweeted this; it now works in every major browser.

More: [Spec: hyperlink auditing](https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing) · [Video](https://www.youtube.com/watch?v=3AIswRtblYY)
