From 9f4921fdc1b9b509c0c4e4aee177ed847a571fa2 Mon Sep 17 00:00:00 2001 From: kwy404 Date: Mon, 9 Nov 2026 18:35:11 -0300 Subject: [PATCH] Remove the generated title when no-title is added after render update() only skipped setting the title while no-title was present, so a title written by an earlier render stayed on the element. Adding no-title later (or setting noTitle = true) left the title in place, and it went stale when datetime changed. The README says no-title removes the title. Remove the title attribute in update() when no-title is present. A custom title is still kept, since that branch only runs for generated titles. --- src/relative-time-element.ts | 6 +++++- test/relative-time.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index e7c9d47..c48f9f9 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -710,7 +710,11 @@ export class RelativeTimeElement extends HTMLElement implements Intl.DateTimeFor const hourCycle = this.hourCycle if (!this.#customTitle) { newTitle = this.#getFormattedTitle(date, locale, timeZone, hourCycle) || '' - if (newTitle && !this.noTitle && newTitle !== oldTitle) this.setAttribute('title', newTitle) + if (this.noTitle) { + this.removeAttribute('title') + } else if (newTitle && newTitle !== oldTitle) { + this.setAttribute('title', newTitle) + } } const duration = elapsedTime(date, this.precision, now) diff --git a/test/relative-time.js b/test/relative-time.js index 186a140..bea06d1 100644 --- a/test/relative-time.js +++ b/test/relative-time.js @@ -296,6 +296,38 @@ suite('relative-time', function () { assert.equal(el.getAttribute('title'), null) }) + test('removes the title when no-title is added after rendering', async () => { + const el = document.createElement('relative-time') + el.setAttribute('datetime', new Date().toISOString()) + await Promise.resolve() + assert.ok(el.getAttribute('title')) + el.setAttribute('no-title', '') + await Promise.resolve() + assert.equal(el.getAttribute('title'), null) + }) + + test('keeps a custom title when no-title is added', async () => { + const el = document.createElement('relative-time') + el.setAttribute('datetime', new Date().toISOString()) + el.setAttribute('title', 'custom') + await Promise.resolve() + el.setAttribute('no-title', '') + await Promise.resolve() + assert.equal(el.getAttribute('title'), 'custom') + }) + + test('sets the title again when no-title is removed', async () => { + const el = document.createElement('relative-time') + el.setAttribute('datetime', new Date().toISOString()) + await Promise.resolve() + const title = el.getAttribute('title') + el.setAttribute('no-title', '') + await Promise.resolve() + el.removeAttribute('no-title') + await Promise.resolve() + assert.equal(el.getAttribute('title'), title) + }) + test('shadowDOM reflects textContent with invalid date', async () => { const el = document.createElement('relative-time') el.textContent = 'A date string'