interestfor 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -5
- package/build.sh +1 -1
- package/package.json +2 -4
- package/src/interestfor.js +52 -10
- package/src/interestfor.min.js +1 -1
- package/test/basic_test.html +3 -0
package/README.md
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
# `interestfor` Polyfill
|
|
2
2
|
|
|
3
3
|
A polyfill for the `interestfor` attribute. This is a new API for "interest
|
|
4
|
-
interactions" that is in the process of being implemented in browsers. It
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
See the explainer for all of the details for the "real" feature:
|
|
4
|
+
interactions" that is in the process of being implemented in browsers. It [shipped](https://chromestatus.com/feature/4530756656562176)
|
|
5
|
+
in Chromium browsers (e.g. Chrome, Edge) in version 142 (October 2025).
|
|
6
|
+
See the MDN documentation for more details:
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
https://developer.mozilla.org/en-US/docs/Web/API/Popover_API/Using_interest_invokers
|
|
10
9
|
|
|
11
10
|
## Usage
|
|
12
11
|
To use this polyfill, simply load it:
|
|
@@ -119,6 +118,11 @@ polyfill (optionally minified) or the native feature. You can run tests directly
|
|
|
119
118
|
from this repo,
|
|
120
119
|
[here](https://mfreed7.github.io/interestfor/test/test.html).
|
|
121
120
|
|
|
121
|
+
## Browser Compatibility
|
|
122
|
+
|
|
123
|
+
This polyfill uses all standard APIs, and is therefore compatible with all
|
|
124
|
+
modern browsers, including Safari and Firefox.
|
|
125
|
+
|
|
122
126
|
## Improvements / Bugs
|
|
123
127
|
|
|
124
128
|
If you find issues with the polyfill, feel free to file them [here](https://github.com/mfreed7/interestfor/issues).
|
package/build.sh
CHANGED
|
@@ -11,7 +11,7 @@ MINIFIED_FILE="${BUILD_DIR}/interestfor.min.js"
|
|
|
11
11
|
OUTPUT_FILE="${SRC_DIR}/interestfor.min.js"
|
|
12
12
|
|
|
13
13
|
# Minify the combined file
|
|
14
|
-
terser "${SOURCE_FILE}" --compress --mangle --output "${MINIFIED_FILE}"
|
|
14
|
+
npx terser "${SOURCE_FILE}" --compress --mangle --output "${MINIFIED_FILE}"
|
|
15
15
|
|
|
16
16
|
cat COPYRIGHT "${MINIFIED_FILE}" > "${OUTPUT_FILE}"
|
|
17
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "interestfor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "A polyfill for the `interestfor` API",
|
|
5
5
|
"keywords": "interestfor, interesttarget, polyfill, hover-triggering",
|
|
6
6
|
"homepage": "https://github.com/mfreed7/interestfor#readme",
|
|
@@ -11,7 +11,5 @@
|
|
|
11
11
|
},
|
|
12
12
|
"author": "Mason Freed <mfreed@mfreed.com>",
|
|
13
13
|
"license": "BSD-3-Clause",
|
|
14
|
-
"main": "src/interestfor.min.js"
|
|
15
|
-
"dependencies": {}
|
|
14
|
+
"main": "src/interestfor.min.js"
|
|
16
15
|
}
|
|
17
|
-
|
package/src/interestfor.js
CHANGED
|
@@ -44,6 +44,35 @@
|
|
|
44
44
|
capture: true,
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
if(!window.InterestEvent) {
|
|
49
|
+
window.InterestEvent = class InterestEvent extends Event {
|
|
50
|
+
#source;
|
|
51
|
+
constructor(type, eventInit = {}) {
|
|
52
|
+
super(type, eventInit);
|
|
53
|
+
const { source } = eventInit;
|
|
54
|
+
if (source != null && !(source instanceof Element)) {
|
|
55
|
+
throw new TypeError(`source must be an element`);
|
|
56
|
+
}
|
|
57
|
+
this.#source = source;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get [Symbol.toStringTag]() {
|
|
61
|
+
return "InterestEvent";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get source() {
|
|
65
|
+
const source = this.#source;
|
|
66
|
+
if (!source) return null;
|
|
67
|
+
// Retarget `source` to the current event's root:
|
|
68
|
+
const sourceRoot = getRootNode(source);
|
|
69
|
+
if (sourceRoot !== getRootNode(this.target || document)) {
|
|
70
|
+
return sourceRoot.host;
|
|
71
|
+
}
|
|
72
|
+
return source;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
47
76
|
|
|
48
77
|
// Enum-like state and source
|
|
49
78
|
const InterestState = {
|
|
@@ -58,6 +87,14 @@
|
|
|
58
87
|
Touch: "touch",
|
|
59
88
|
};
|
|
60
89
|
|
|
90
|
+
function getRootNode(node) {
|
|
91
|
+
if (node && typeof node.getRootNode === "function") {
|
|
92
|
+
return node.getRootNode();
|
|
93
|
+
}
|
|
94
|
+
if (node && node.parentNode) return getRootNode(node.parentNode);
|
|
95
|
+
return node;
|
|
96
|
+
}
|
|
97
|
+
|
|
61
98
|
// Gain or lose interest
|
|
62
99
|
function GainOrLoseInterest(invoker, target, newState) {
|
|
63
100
|
if (!invoker || !target) {
|
|
@@ -208,7 +245,8 @@
|
|
|
208
245
|
if (data.state !== InterestState.NoInterest) {
|
|
209
246
|
throw new Error("Invalid state");
|
|
210
247
|
}
|
|
211
|
-
target.dispatchEvent(new
|
|
248
|
+
const shouldContinue = target.dispatchEvent(new InterestEvent(interestEventName, { source: invoker }));
|
|
249
|
+
if (!shouldContinue) return;
|
|
212
250
|
try {
|
|
213
251
|
target.showPopover({ source: invoker });
|
|
214
252
|
} catch {}
|
|
@@ -227,12 +265,15 @@
|
|
|
227
265
|
if (!isPlainHint(target)) {
|
|
228
266
|
invoker.setAttribute("aria-expanded", "true");
|
|
229
267
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
268
|
+
if (getComputedStyle(invoker).anchorName === 'none' &&
|
|
269
|
+
getComputedStyle(target).positionAnchor === 'auto') {
|
|
270
|
+
const anchorName = `--interest-anchor-${Math.random()
|
|
271
|
+
.toString(36)
|
|
272
|
+
.substring(2)}`;
|
|
273
|
+
invoker.style.anchorName = anchorName;
|
|
274
|
+
target.style.positionAnchor = anchorName;
|
|
275
|
+
data.anchorName = anchorName;
|
|
276
|
+
}
|
|
236
277
|
break;
|
|
237
278
|
default:
|
|
238
279
|
throw new Error("Invalid state");
|
|
@@ -240,13 +281,14 @@
|
|
|
240
281
|
return true;
|
|
241
282
|
}
|
|
242
283
|
|
|
243
|
-
function clearState(invoker) {
|
|
284
|
+
function clearState(invoker, force = false) {
|
|
244
285
|
const data = invoker[dataField];
|
|
245
286
|
clearTimeout(data.gainedTimer);
|
|
246
287
|
clearTimeout(data.lostTimer);
|
|
247
288
|
if (data.state !== InterestState.NoInterest) {
|
|
248
289
|
const target = GetInterestForTarget(invoker);
|
|
249
|
-
target.dispatchEvent(new
|
|
290
|
+
const shouldContinue = target.dispatchEvent(new InterestEvent(loseInterestEventName, { source: invoker }));
|
|
291
|
+
if (!force && !shouldContinue) return;
|
|
250
292
|
try {
|
|
251
293
|
target.hidePopover();
|
|
252
294
|
} catch {}
|
|
@@ -408,7 +450,7 @@
|
|
|
408
450
|
document.body.addEventListener("keydown", (e) => {
|
|
409
451
|
if (e.key === "Escape") {
|
|
410
452
|
invokersWithInterest.forEach((invoker) => {
|
|
411
|
-
clearState(invoker);
|
|
453
|
+
clearState(invoker, true);
|
|
412
454
|
});
|
|
413
455
|
}
|
|
414
456
|
});
|
package/src/interestfor.min.js
CHANGED
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// LICENSE file in the root directory of this source tree.
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
!function(){const e="interest",t="loseinterest",n="--interest-delay-start",r="--interest-delay-end",o="--interest-delay",
|
|
8
|
+
!function(){const e="interest",t="loseinterest",n="--interest-delay-start",r="--interest-delay-end",o="--interest-delay",s="__interestForData",i="__interestForTargetData",a=new Set;let l=!1;if(window.interestForPolyfillInstalled)return;window.interestForPolyfillInstalled=!0;const c=HTMLButtonElement.prototype.hasOwnProperty("interestForElement");if(c&&!window.interestForUsePolyfillAlways)return;if(c){const n=e=>{e.isTrusted&&(e.preventDefault(),e.stopImmediatePropagation())};document.body.addEventListener(e,n,{capture:!0}),document.body.addEventListener(t,n,{capture:!0})}window.InterestEvent||(window.InterestEvent=class extends Event{#e;constructor(e,t={}){super(e,t);const{source:n}=t;if(null!=n&&!(n instanceof Element))throw new TypeError("source must be an element");this.#e=n}get[Symbol.toStringTag](){return"InterestEvent"}get source(){const e=this.#e;if(!e)return null;const t=p(e);return t!==p(this.target||document)?t.host:e}});const u="none",d="full",m="hover",f="dehover",h="focus",g="blur";function p(e){return e&&"function"==typeof e.getRootNode?e.getRootNode():e&&e.parentNode?p(e.parentNode):e}function y(t,n,r){if(!t||!n)return!1;if(!t.isConnected||T(t)!==n||r===u&&b(n)!==t)return!1;if(r!==u){const o=b(n);if(o){if(o===t)return o[s].clearLostTask(),!1;if(!y(o,n,u))return!1;if(!t.isConnected||T(t)!==n)return!1}return function(t,n){const r=t[s],o=T(t);if(n!==d)throw new Error("Invalid state");if(r.state!==u)throw new Error("Invalid state");if(!o.dispatchEvent(new InterestEvent(e,{source:t})))return;try{o.showPopover({source:t})}catch{}if(r.state=d,o[i]||(o[i]={}),o[i].invoker=t,o.hasAttribute("popover")&&(o[i].toggleListener=L,o.addEventListener("toggle",L)),a.add(t),t.classList.add("interest-source"),o.classList.add("interest-target"),P(o)||t.setAttribute("aria-expanded","true"),"none"===getComputedStyle(t).anchorName&&"auto"===getComputedStyle(o).positionAnchor){const e=`--interest-anchor-${Math.random().toString(36).substring(2)}`;t.style.anchorName=e,o.style.positionAnchor=e,r.anchorName=e}return!0}(t,r)}return k(t),!0}function v(e){const t=1e3*w(e,r);!isFinite(t)||t<0||(e[s].clearLostTask(),e[s].lostTimer=setTimeout(()=>{y(e,T(e),u)},t))}function b(e){const t=e[i]?.invoker||null;return t&&t[s]?.state!==u?t:null}function T(e){const t=e.getAttribute("interestfor");return document.getElementById(t)}function L(e){const t=e.target;if("closed"===e.newState){const e=t[i]?.invoker;e&&y(e,t,u)}}function E(e){const t=String(e).trim(),n=t.match(/^([\d.]+)s$/);if(n)return parseFloat(n[1]);const r=t.match(/^([\d.]+)ms$/);return r?parseFloat(r[1])/1e3:parseFloat(t)||0}function w(e,t){const r=getComputedStyle(e),s=r.getPropertyValue(t).trim();if("normal"!==s.toLowerCase())return E(s);const i=r.getPropertyValue(o).trim();if(i&&"normal"!==i.toLowerCase()){const e=i.split(/\s+/).filter(e=>e.length>0);if(e.length>0){const r=e[0],o=e.length>1?e[1]:r;let s;if(s=t===n?r:o,"normal"!==s.toLowerCase())return E(s)}}return t===n?.5:.25}function k(e,n=!1){const r=e[s];if(clearTimeout(r.gainedTimer),clearTimeout(r.lostTimer),r.state!==u){const o=T(e),s=o.dispatchEvent(new InterestEvent(t,{source:e}));if(!n&&!s)return;try{o.hidePopover()}catch{}o[i]?.toggleListener&&o.removeEventListener("toggle",o[i].toggleListener),o[i]=null,a.delete(e),e.classList.remove("interest-source"),o.classList.remove("interest-target"),P(o)||e.setAttribute("aria-expanded","false"),r.anchorName&&(e.style.anchorName="",o.style.positionAnchor="",r.anchorName=null),r.state=u}}const A=["a[href]","area[href]","input:not([disabled])","select:not([disabled])","textarea:not([disabled])","button:not([disabled])","iframe","object","embed","[contenteditable]",'[tabindex]:not([tabindex="-1"])'].join(",");function P(e){if("hint"!==e.getAttribute("popover")?.toLowerCase())return!1;if(e.querySelector(A))return!1;if(e.querySelector("h1,h2,h3,h4,h5,h6,ul,ol,li,table,nav,header,footer,main,aside,article,section,form,blockquote,details,summary,dialog"))return!1;const t=e.querySelectorAll("[role]");for(const e of t){const t=e.getAttribute("role").toLowerCase();if(!["presentation","none","generic","image"].includes(t))return!1}return!0}function S(e){if(e[s])return;e[s]={state:u,gainedTimer:null,lostTimer:null,longPressTimer:null,clearGainedTask(){clearTimeout(this.gainedTimer)},clearLostTask(){clearTimeout(this.lostTimer)}};const t=T(e);t&&function(e,t){P(t)?e.setAttribute("aria-describedby",t.id):(e.setAttribute("aria-details",t.id),e.setAttribute("aria-expanded","false"),t.hasAttribute("role")||t.setAttribute("role","tooltip"))}(e,t)}function C(e,t){if(l)return;if(!e.isConnected)return;if(!T(e)){const n=e.closest(".interest-target");if(n){const r=b(n);r&&(t===m||t===h?r[s].clearLostTask():t!==g&&e.matches(":hover")||v(r))}return}S(e);const r=e[s],o=b(e);t===m||t===h?(r.clearLostTask&&r.clearLostTask(),o&&o[s].clearLostTask(),function(e,t){const r=1e3*w(e,n);!isFinite(r)||r<0||(e[s].clearGainedTask(),e[s].gainedTimer=setTimeout(()=>{y(e,T(e),t)},r))}(e,d)):(r.clearGainedTask&&r.clearGainedTask(),r.state!==u&&v(e),o&&(o[s].clearGainedTask(),t!==g&&e.matches(":hover")||v(o)))}function x(){!function(){const e=document.createElement("style");e.textContent=`@property ${n} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${r} {syntax: "normal | <time>"; inherits: false; initial-value: normal;}\n @property ${o} {syntax: "[ normal | <time> ]{1,2}"; inherits: false; initial-value: normal;}`,document.head.appendChild(e),document[s]={globalPropsStyle:e}}(),function(){const e=(e,t)=>{for(let n=e.target;n;n=n.parentElement)C(n,t)};document.body.addEventListener("mouseover",t=>e(t,m)),document.body.addEventListener("mouseout",t=>e(t,f)),document.body.addEventListener("focusin",t=>e(t,h)),document.body.addEventListener("focusout",t=>e(t,g)),document.body.addEventListener("keydown",e=>{"Escape"===e.key&&a.forEach(e=>{k(e,!0)})}),document.body.addEventListener("touchstart",e=>{l=!0;const t=e.target.closest("button[interestfor]");t&&(S(t),t[s].longPressTimer=setTimeout(()=>{y(t,T(t),d),t[s].longPressTimer=null},500))});const t=e=>{const t=e.target.closest("button[interestfor]");t&&t[s]?.longPressTimer&&(clearTimeout(t[s].longPressTimer),t[s].longPressTimer=null)};document.body.addEventListener("touchend",e=>{t(e),l=!1}),document.body.addEventListener("touchmove",t)}(),console.log(`interestfor polyfill installed (native: ${c}).`)}"complete"===document.readyState?x():window.addEventListener("load",x)}();
|