diff --git a/src/relative-time-element.ts b/src/relative-time-element.ts index be35920..7103a8a 100644 --- a/src/relative-time-element.ts +++ b/src/relative-time-element.ts @@ -28,17 +28,26 @@ function getUnitFactor(ms: number): number { const dateObserver = new (class { elements: Set = new Set() + time = 0 observe(element: RelativeTimeElement) { if (this.elements.has(element)) return this.elements.add(element) - this.update() + const date = element.date + if (date && date.getTime()) { + const ms = getUnitFactor(date.getTime()) + const time = Date.now() + ms + if (time < this.time) { + clearTimeout(this.timer) + this.timer = setTimeout(() => this.update, ms) + this.time = time + } + } } unobserve(element: RelativeTimeElement) { if (!this.elements.has(element)) return this.elements.delete(element) - this.update() } timer: ReturnType = -1 as unknown as ReturnType @@ -52,8 +61,9 @@ const dateObserver = new (class { nearestDistance = Math.min(nearestDistance, distance) timeEl.update() } - const ms = Math.min(60 * 60 * 1000, nearestDistance) - this.timer = setTimeout(() => this.update(), ms) + this.time = Math.min(60 * 60 * 1000, nearestDistance) + this.timer = setTimeout(() => this.update(), this.time) + this.time += Date.now() } })() @@ -298,11 +308,10 @@ export default class RelativeTimeElement extends HTMLElement implements Intl.Dat } // Internal: Refresh the time element's formatted date when an attribute changes. - attributeChangedCallback(attrName: string): void { - if (attrName === 'title') { - this.#customTitle = true - } - this.update() + attributeChangedCallback(attrName: string, oldValue: unknown, newValue: unknown): void { + if (oldValue === newValue) return + if (attrName === 'title') this.#customTitle = true + if (!this.#customTitle) this.update() } update() { diff --git a/test/relative-time.js b/test/relative-time.js index 3c39f9f..0573b2f 100644 --- a/test/relative-time.js +++ b/test/relative-time.js @@ -1,5 +1,5 @@ import {assert} from '@open-wc/testing' -import '../src/relative-time-element.ts' +import RelativeTimeElement from '../src/relative-time-element.ts' suite('relative-time', function () { let dateNow @@ -38,6 +38,26 @@ suite('relative-time', function () { } }) + test('does not call update() frequently with attributeChangedCallback', () => { + let counter = 0 + const el = document.createElement('relative-time') + el.update = function () { + counter += 1 + return RelativeTimeElement.prototype.update.call(this) + } + assert.equal(counter, 0) + el.setAttribute('datetime', new Date().toISOString()) + assert.equal(counter, 1) + el.setAttribute('datetime', el.getAttribute('datetime')) + assert.equal(counter, 1) + el.disconnectedCallback() + assert.equal(counter, 1) + el.setAttribute('title', 'custom') + assert.equal(counter, 1) + el.setAttribute('title', 'another custom') + assert.equal(counter, 1) + }) + test("doesn't error when no date is provided", function () { const element = document.createElement('relative-time') assert.doesNotThrow(() => element.attributeChangedCallback('datetime', null, null))