From 54e35f06e7a0f3a81ba512c7f79bfdbd8b8b182d Mon Sep 17 00:00:00 2001 From: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> Date: Fri, 5 Jun 2026 22:18:55 +0200 Subject: [PATCH 1/2] feat: implement lazy loading for combobox technology icons using IntersectionObserver Signed-off-by: Max Ostapenko <1611259+max-ostapenko@users.noreply.github.com> --- src/js/techreport/combobox.js | 42 ++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/js/techreport/combobox.js b/src/js/techreport/combobox.js index 27aafdbf..5feeac56 100644 --- a/src/js/techreport/combobox.js +++ b/src/js/techreport/combobox.js @@ -20,6 +20,33 @@ class ComboBox { const rows = data || this.data; // Add options in the dropdown list const listbox = this.element.querySelector('[role="listbox"]'); + + const hasObserver = typeof IntersectionObserver !== 'undefined'; + if (hasObserver) { + if (!this.observer) { + this.observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const option = entry.target; + const icon = option.dataset.icon; + if (icon && !option.querySelector('img')) { + const logo = document.createElement('img'); + logo.setAttribute('alt', ''); + logo.setAttribute('src', `https://cdn.httparchive.org/v1/static/icons/${encodeURI(icon)}`); + option.append(logo); + } + this.observer.unobserve(option); + } + }); + }, { + root: listbox, + rootMargin: '100px', + }); + } else { + this.observer.disconnect(); + } + } + rows.forEach((row, index) => { const icon = row.icon; const option = document.createElement('div'); @@ -30,11 +57,16 @@ class ComboBox { option.textContent = row.technology; option.id = `${this.element.dataset.id}-${row.technology.replaceAll(' ','-')}`; if(icon) { - const logo = document.createElement('img'); - logo.setAttribute('alt', ''); - logo.setAttribute('src', `https://cdn.httparchive.org/v1/static/icons/${icon}`); - logo.setAttribute('loading', 'lazy'); - option.append(logo); + if (hasObserver) { + option.dataset.icon = icon; + this.observer.observe(option); + } else { + const logo = document.createElement('img'); + logo.setAttribute('alt', ''); + logo.setAttribute('src', `https://cdn.httparchive.org/v1/static/icons/${encodeURI(icon)}`); + logo.setAttribute('loading', 'lazy'); + option.append(logo); + } } if(this.selected.includes(row.technology)) { option.setAttribute('aria-selected', true); From cf5e356cbe0104392cd3b959a3f98d263121fcf4 Mon Sep 17 00:00:00 2001 From: Barry Pollard Date: Mon, 8 Jun 2026 17:07:57 +0100 Subject: [PATCH 2/2] Update src/js/techreport/combobox.js --- src/js/techreport/combobox.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/techreport/combobox.js b/src/js/techreport/combobox.js index 5feeac56..07d26b7d 100644 --- a/src/js/techreport/combobox.js +++ b/src/js/techreport/combobox.js @@ -21,6 +21,8 @@ class ComboBox { // Add options in the dropdown list const listbox = this.element.querySelector('[role="listbox"]'); + // Typing in the filter box does work for loading=lazy + // so use a manual IntersectionObserver for now. const hasObserver = typeof IntersectionObserver !== 'undefined'; if (hasObserver) { if (!this.observer) {