Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions src/lazy-define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,33 @@ const strategies: Record<string, Strategy> = {

type ElementLike = Element | Document | ShadowRoot

const timers = new WeakMap<ElementLike, number>()
const pendingElements = new Set<ElementLike>()
let scanTimer: number | null = null

function scan(element: ElementLike) {
cancelAnimationFrame(timers.get(element) || 0)
timers.set(
element,
requestAnimationFrame(() => {
pendingElements.add(element)
Comment thread
alexus37 marked this conversation as resolved.
if (scanTimer != null) return
scanTimer = requestAnimationFrame(() => {
scanTimer = null
const elements = new Set(pendingElements)
pendingElements.clear()
if (!dynamicElements.size) {
return
}
outer: for (const el of elements) {
for (const tagName of dynamicElements.keys()) {
const child: Element | null =
element instanceof Element && element.matches(tagName) ? element : element.querySelector(tagName)
const child: Element | null = el instanceof Element && el.matches(tagName) ? el : el.querySelector(tagName)
if (customElements.get(tagName) || child) {
const strategyName = (child?.getAttribute('data-load-on') || 'ready') as keyof typeof strategies
const strategy = strategyName in strategies ? strategies[strategyName] : strategies.ready
// eslint-disable-next-line github/no-then
for (const cb of dynamicElements.get(tagName) || []) strategy(tagName).then(cb)
dynamicElements.delete(tagName)
timers.delete(element)
if (!dynamicElements.size) break outer
}
}
})
)
}
})
Comment thread
alexus37 marked this conversation as resolved.
}

let elementLoader: MutationObserver
Expand Down
32 changes: 32 additions & 0 deletions test/lazy-define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,38 @@ describe('lazyDefine', () => {
expect(onDefine3).to.have.callCount(1)
})

it('coalesces multiple added elements into a single rAF callback', async () => {
const onDefine = spy()
lazyDefine('coalesce-test-element', onDefine)

const rafSpy = spy(window, 'requestAnimationFrame')
const callsBefore = rafSpy.callCount

await fixture(html`
<div>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
<coalesce-test-element></coalesce-test-element>
</div>
`)

await animationFrame()

const rafCallsFromScan = rafSpy.callCount - callsBefore
rafSpy.restore()

// Should use at most a few rAF calls, not one per element
expect(rafCallsFromScan).to.be.lessThan(5)
expect(onDefine).to.be.callCount(1)
})
Comment thread
alexus37 marked this conversation as resolved.

it('lazy loads elements in shadow roots', async () => {
const onDefine = spy()
lazyDefine('nested-shadow-element', onDefine)
Expand Down
Loading