diff --git a/README.md b/README.md index a754e909..471d5bbf 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Do not specify supported browsers and their versions in code comments or prose, - The "insert-adjacent" directory contains basic demos for [insertAdjacentElement](https://mdn.github.io/dom-examples/insert-adjacent/insertAdjacentElement.html) and [insertAdjacentText](https://mdn.github.io/dom-examples/insert-adjacent/insertAdjacentText.html). +- The "interest-invokers" directory is for [interest invoker](https://developer.mozilla.org/docs/Web/API/Popover_API/Interest_invokers) examples. Go to the [Interest invoker examples index](https://mdn.github.io/dom-examples/interest-invokers/) to see what's available. + - The "launch-handler" directory contains a demo for the [Launch Handler API](https://developer.mozilla.org/en-US/docs/Web/API/Launch_Handler_API). [View the demo live](https://mdn.github.io/dom-examples/launch-handler/). This example was originally published on Glitch by [Thomas Steiner](https://front-end.social/@tomayac@toot.cafe). - The "local-font-access" directory contains a demo for the [Local Font Access API](https://developer.mozilla.org/en-US/docs/Web/API/Local_Font_Access_API). [View the demo live](https://mdn.github.io/dom-examples/local-font-access/). @@ -123,7 +125,6 @@ Do not specify supported browsers and their versions in code comments or prose, - Another example of view transitions in a [simple multiple-page app](https://mdn.github.io/dom-examples/view-transitions/mpa-homepage/) - [The `match-element` value](https://mdn.github.io/dom-examples/view-transitions/match-element/) for the `view-transition-name` property - - The "web-share" directory contains a basic demo to show usage of the [Web Share API](https://developer.mozilla.org/docs/Web/API/Navigator/share). [View the demo live](https://mdn.github.io/dom-examples/web-share/). - The "web-workers" directory contains several demos that show how [Web Workers](https://developer.mozilla.org/docs/Web/API/Web_Workers_API) operate. For example, view the live [simple web worker demo](https://mdn.github.io/dom-examples/web-workers/simple-web-worker/). diff --git a/interest-invokers/index.html b/interest-invokers/index.html new file mode 100644 index 00000000..81817ef8 --- /dev/null +++ b/interest-invokers/index.html @@ -0,0 +1,41 @@ + + + + + Interest invoker examples + + +

MDN interest invoker examples

+ +

+ This set of examples demonstrates usage of + Interest invokers. +

+ + + + diff --git a/interest-invokers/popover-examples/chris-mills.jpg b/interest-invokers/popover-examples/chris-mills.jpg new file mode 100644 index 00000000..0f9fdf37 Binary files /dev/null and b/interest-invokers/popover-examples/chris-mills.jpg differ diff --git a/interest-invokers/popover-examples/index.html b/interest-invokers/popover-examples/index.html new file mode 100644 index 00000000..516e59c2 --- /dev/null +++ b/interest-invokers/popover-examples/index.html @@ -0,0 +1,40 @@ + + + + + interestfor example + + + + + +

+ I think + @chrisdavidmills + should know about this. +

+ +
+
+ chris mills +
+

Chris Mills

+

+ Independent tech writer and web technology tinkerer, working on MDN + on behalf of Google and Mozilla. A11y and open standards advocate. + Heavy metal drummer. +

+

🌍 Greenfield, UK

+
+
+
+ +

+ +
+

This button does absolutely nothing.

+
+ + diff --git a/interest-invokers/popover-examples/index.js b/interest-invokers/popover-examples/index.js new file mode 100644 index 00000000..7b68d093 --- /dev/null +++ b/interest-invokers/popover-examples/index.js @@ -0,0 +1,35 @@ +const interestLink = document.querySelector("[interestfor='user-info']"); +const interestButton = document.querySelector("[interestfor='button-tooltip']"); +const userInfo = document.getElementById("user-info"); +const buttonTooltip = document.getElementById("button-tooltip"); + +// Feature detection + +const supported = + HTMLButtonElement.prototype.hasOwnProperty("interestForElement"); +if (!supported) { + document.querySelector("html").classList.add("no-interest-invokers"); +} + +// interestForElement example + +console.log(interestLink.interestForElement); +console.log(interestButton.interestForElement); + +// Event examples + +userInfo.addEventListener("interest", reportInterest); +buttonTooltip.addEventListener("interest", reportInterest); +userInfo.addEventListener("loseinterest", reportInterest); +buttonTooltip.addEventListener("loseinterest", reportInterest); + +function reportInterest(e) { + let action; + if (e.type === "interest") { + action = "Interest gained"; + } else { + action = "Interest lost"; + } + + console.log(`${action} on ${e.source.tagName}`); +} diff --git a/interest-invokers/popover-examples/styles.css b/interest-invokers/popover-examples/styles.css new file mode 100644 index 00000000..43c35790 --- /dev/null +++ b/interest-invokers/popover-examples/styles.css @@ -0,0 +1,96 @@ +html { + font-family: sans-serif; +} + +* { + box-sizing: border-box; +} + +/* Browser support banner */ + +.no-interest-invokers body::before { + content: "Your browser doesn't support interest invokers."; + background-color: wheat; + display: block; + padding: 10px 0; + width: 100%; + text-align: center; +} + +/* Link interestfor styles */ + +a[interestfor] { + interest-delay: 1s 2s; +} + +/* Shared styles across popovers */ + +#user-info, +#button-tooltip { + border: 1px solid lightgray; + border-radius: 5px; + padding: 0 10px; + margin: 5px; + background-color: white; + font-size: 0.8rem; +} + +#user-info { + position-area: bottom right; +} + +#user-info .wrapper { + display: flex; + align-items: center; + gap: 20px; + width: 480px; + font-size: 0.8rem; +} + +#user-info img { + margin: 10px 0; + border: 1px solid lightgray; + border-radius: 5px; +} + +/* button interestfor styles */ + +button[interestfor="button-tooltip"] { + border: 1px solid transparent; + padding: 5px 10px; + border-radius: 5px; + color: white; + background-color: rgb(200 0 0); +} + +/* Applies styles to the interest invoker, but only when the user is showing interest */ +button:interest-source { + background-color: rgb(255 0 0); +} + +button[interestfor="button-tooltip"]:active { + color: rgb(255 0 0); + border: 1px solid rgb(255 0 0); + background-color: white; +} + +#button-tooltip { + position-area: right; +} + +/* animation for interest targets */ + +[popover]:interest-target { + opacity: 1; +} + +[popover] { + opacity: 0; + transition: all 0.7s allow-discrete; +} + +@starting-style { + [popover]:interest-target { + opacity: 0; + } +} diff --git a/interest-invokers/stock-moving/index.html b/interest-invokers/stock-moving/index.html new file mode 100644 index 00000000..65d73679 --- /dev/null +++ b/interest-invokers/stock-moving/index.html @@ -0,0 +1,60 @@ + + + + + interestfor stock moving example + + + + + +

interestfor stock moving example

+
+

+
+

Stock to keep

+
+ + + + + +
+
+
+

Stock to sell

+
+
+
+ + diff --git a/interest-invokers/stock-moving/index.js b/interest-invokers/stock-moving/index.js new file mode 100644 index 00000000..c3251abc --- /dev/null +++ b/interest-invokers/stock-moving/index.js @@ -0,0 +1,102 @@ +// Feature detection + +const supported = + HTMLButtonElement.prototype.hasOwnProperty("interestForElement"); +if (!supported) { + document.querySelector("html").classList.add("no-interest-invokers"); +} + +// Variable and handler definition + +const interestBtns = document.querySelectorAll("button"); +const btnTooltip = document.getElementById("button-tooltip"); +const keepList = document.querySelector(".keep"); +const sellList = document.querySelector(".sell"); + +let interval; +let stockToMove = 0; + +btnTooltip.addEventListener("interest", incrementStock); +btnTooltip.addEventListener("loseinterest", resetStock); +interestBtns.forEach((btn) => btn.addEventListener("click", moveStock)); + +// Increment the amount of stock to move when interest is shown +function incrementStock(e) { + const sourceElem = e.source; + stockToMove = 0; + + function updateStockCount() { + // Only increment stockToMove if it is smaler than the entire available stock + let stock = Number(sourceElem.getAttribute("data-stock")); + if (stockToMove < stock) { + stockToMove++; + btnTooltip.textContent = `Stock to move: ${stockToMove}`; + } + } + + // Immediately increment stock to move, and then do it repeated every 250ms + updateStockCount(); + interval = setInterval(updateStockCount, 250); +} + +// Reset the amount of stock to move when interest is lost +function resetStock(e) { + // Clear the interval and the tooltip's text content, ready for next time + clearInterval(interval); + btnTooltip.textContent = ""; +} + +// Move the stock when the button is actually clicked +function moveStock(e) { + // Determine which list we are moving the stock item to + let targetList; + if (e.target.parentNode === keepList) { + targetList = sellList; + } else { + targetList = keepList; + } + + // number of items and product to move + const newStock = stockToMove; + const newProduct = e.target.getAttribute("data-product"); + + // Determine whether a stack of that item already + // exists at the place to move the item to + const targetProductsList = Array.from(targetList.children); + const existingNewProduct = targetProductsList.find( + (element) => element.getAttribute("data-product") === newProduct + ); + + // If so, just add the items to the existing stack + if (existingNewProduct) { + const totalStock = + Number(existingNewProduct.getAttribute("data-stock")) + newStock; + existingNewProduct.setAttribute("data-stock", totalStock); + existingNewProduct.textContent = `${newProduct} (${totalStock})`; + } else { + // If not, create a new stack and append it to the list + const newBtn = document.createElement("button"); + newBtn.setAttribute("interestFor", "button-tooltip"); + newBtn.setAttribute("data-stock", newStock); + newBtn.setAttribute("data-product", newProduct); + newBtn.textContent = `${newProduct} (${newStock})`; + newBtn.addEventListener("click", moveStock); + targetList.appendChild(newBtn); + } + + // Update the stack the stock was moved FROM to show the remaining items + const oldStock = Number(e.target.getAttribute("data-stock")) - stockToMove; + e.target.setAttribute("data-stock", oldStock); + e.target.textContent = `${newProduct} (${oldStock})`; + + // Unfocus the button interest was being shown in. If you don't do this, you get weird behavior + // when it was focused via the keyboard — the interestfor target element starts incrementing again + // immediately, and if the stack goes to 0 and disappears (see next block), the focus shifts to the + // body and you get the interestfor target appearing at the top of the viewport! + e.target.blur(); + + // If the stack the stock was moved from has no remaining items, remove it from the DOM + if (e.target.getAttribute("data-stock") === "0") { + e.target.remove(); + } +} diff --git a/interest-invokers/stock-moving/styles.css b/interest-invokers/stock-moving/styles.css new file mode 100644 index 00000000..e9f4a4b2 --- /dev/null +++ b/interest-invokers/stock-moving/styles.css @@ -0,0 +1,58 @@ +html { + font-family: sans-serif; +} + +* { + box-sizing: border-box; +} + +/* Browser support banner */ + +.no-interest-invokers body::before { + content: "Your browser doesn't support interest invokers."; + background-color: wheat; + display: block; + padding: 10px 0; + width: 100%; + text-align: center; +} + +/* Layout styles */ + +.wrapper { + max-width: 640px; + margin: 0 auto; + display: flex; +} + +.wrapper section { + flex: 1; +} + +/* interestfor styles */ + +h1, +h2 { + text-align: center; +} + +button[interestfor] { + display: block; + padding: 10px; + border-radius: 5px; + border: 1px solid lightgray; + width: 50%; + margin: 10px auto; + /* Needs to be set on the interest invoker element (with interestfor set on it), not the popover */ + interest-delay: 1s 0s; +} + +#button-tooltip { + border: 1px solid lightgray; + border-radius: 5px; + padding: 10px; + margin: 1px; + background-color: white; + font-size: 0.8rem; + position-area: bottom; +} diff --git a/interest-invokers/style-preview/index.html b/interest-invokers/style-preview/index.html new file mode 100644 index 00000000..f02a11d8 --- /dev/null +++ b/interest-invokers/style-preview/index.html @@ -0,0 +1,23 @@ + + + + + interestfor without popover + + + + + +
+ + + + + +
+
+

Style preview panel

+

This is kinda fun, no?

+
+ + diff --git a/interest-invokers/style-preview/index.js b/interest-invokers/style-preview/index.js new file mode 100644 index 00000000..3d39f846 --- /dev/null +++ b/interest-invokers/style-preview/index.js @@ -0,0 +1,31 @@ +const stylePanel = document.getElementById("style-panel"); +const buttons = document.querySelectorAll("button"); + +let prevStyle = "black-white"; + +// Feature detection + +const supported = + HTMLButtonElement.prototype.hasOwnProperty("interestForElement"); +if (!supported) { + document.querySelector("html").classList.add("no-interest-invokers"); +} + +// Event examples + +stylePanel.addEventListener("interest", sampleStyle); +stylePanel.addEventListener("loseinterest", revertStyle); +buttons.forEach((button) => button.addEventListener("click", setStyle)); + +function sampleStyle(e) { + e.target.className = e.source.className; +} + +function revertStyle(e) { + e.target.className = prevStyle; +} + +function setStyle(e) { + stylePanel.className = e.target.className; + prevStyle = e.target.className; +} diff --git a/interest-invokers/style-preview/styles.css b/interest-invokers/style-preview/styles.css new file mode 100644 index 00000000..4e0f1a2c --- /dev/null +++ b/interest-invokers/style-preview/styles.css @@ -0,0 +1,105 @@ +html { + font-family: sans-serif; +} + +* { + box-sizing: border-box; +} + +/* Browser support banner */ + +.no-interest-invokers body::before { + content: "Your browser doesn't support interest invokers."; + background-color: wheat; + display: block; + padding: 10px 0; + width: 100%; + text-align: center; +} + +/* General styles */ + +body { + width: 640px; + margin: 0 auto; +} + +body > div { + display: flex; + gap: 5px; + margin-top: 20px; +} + +button { + flex: 1; + padding: 5px; + border-radius: 3px; +} + +#style-panel { + padding: 20px; + border-radius: 30px; + margin-top: 20px; + border-width: 10px; + corner-shape: scoop; +} + +h2 { + margin-top: 0; + text-align: center; + letter-spacing: 5px; +} + +p { + margin-bottom: 0; + text-align: center; + font-weight: bold; + letter-spacing: 3px; +} + +/* Color classes */ + +.black-white { + color: black; + background-color: white; + border: 2px solid black; +} + +.bubblegum { + color: #fff8f0; + background-color: #ef476f; + border: 2px solid #fff8f0; + box-shadow: 0 0 2px #ef476f; +} + +.purple-haze { + color: #8a1c7c; + background-color: #f0bcd4; + border: 2px solid #8a1c7c; +} + +.blaze { + color: #f2e94e; + background-color: #7e6b8f; + border: 2px solid #f2e94e; +} + +.mint-brown { + color: #41463d; + background-color: #1cfeba; + border: 2px solid #41463d; +} + +/* Applies styles to the interest invoker, but only when the user is showing interest */ + +button:interest-source { + background-color: black; + color: white; + border: 2px solid black; +} + +/* animation for style panel */ + +#style-panel { + transition: all 0.7s; +}