Changeset 3248595
- Timestamp:
- 02/28/2025 04:11:27 PM (12 months ago)
- Location:
- accessibility-new-window-warnings
- Files:
-
- 8 edited
- 1 copied
-
tags/1.1.0 (copied) (copied from accessibility-new-window-warnings/trunk)
-
tags/1.1.0/README.txt (modified) (2 diffs)
-
tags/1.1.0/accessibility-new-window-warnings.php (modified) (2 diffs)
-
tags/1.1.0/assets/js/accessibility-new-window-warnings-min.js (modified) (1 diff)
-
tags/1.1.0/assets/js/accessibility-new-window-warnings.js (modified) (3 diffs)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/accessibility-new-window-warnings.php (modified) (2 diffs)
-
trunk/assets/js/accessibility-new-window-warnings-min.js (modified) (1 diff)
-
trunk/assets/js/accessibility-new-window-warnings.js (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
accessibility-new-window-warnings/tags/1.1.0/README.txt
r3174459 r3248595 3 3 Tags: accessibility, accessible, wcag, ada, a11y, section 508, links, open new window, open new tab 4 4 Requires at least: 6.4.0 5 Tested up to: 6. 6.26 Stable tag: 1. 0.105 Tested up to: 6.7.2 6 Stable tag: 1.1.0 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 75 75 == Changelog == 76 76 77 = 1.1.0 = 78 * Fixed - tooltips can now be dismissed using the Escape key, ensuring better accessibility 79 * Fixed - tooltip content is now hoverable, preventing accidental dismissal 80 * Refactored - code for better performance and maintainability using modern JavaScript practices 81 77 82 = 1.0.10 = 78 83 * Added - detection and processing of window.open() links with consistent tooltip/ARIA label handling for both target="_blank" and window.open() -
accessibility-new-window-warnings/tags/1.1.0/accessibility-new-window-warnings.php
r3174459 r3248595 4 4 * Plugin URI: https://a11ychecker.com 5 5 * Description: Make links that open in a new window accessible by adding a warning. 6 * Version: 1. 0.106 * Version: 1.1.0 7 7 * Author: Equalize Digital 8 8 * Author URI: https://equalizedigital.com … … 14 14 */ 15 15 16 define( 'ANWW_VERSION', '1. 0.10' );16 define( 'ANWW_VERSION', '1.1.0' ); 17 17 define( 'ANWW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 18 18 define( 'ANWW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); -
accessibility-new-window-warnings/tags/1.1.0/assets/js/accessibility-new-window-warnings-min.js
r3174459 r3248595 1 !function( $){"use strict";var n;function e(){var e="";$(".anww-external-link-icon").remove(),$("a").each((function(){var t=$(this).attr("onclick");if("_blank"===$(this).attr("target")&&(o($(this)),l($(this)),d($(this)),!0),t&&t.includes("window.open")){var a=t.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),i=a?a[1]:"";"_blank"!==i&&""!==i||(o($(this)),l($(this)),d($(this)),!0)}function o(n){$(":header",n).length?$(":header",n).append('<i class="anww-external-link-icon" aria-hidden="true"></i>'):n.append('<i class="anww-external-link-icon" aria-hidden="true"></i>')}function l(n){n.attr("aria-label")?e=n.attr("aria-label"):$("img",n).length?e=n.find("img").attr("alt"):n.text()&&(e=n.text()),e?(e=e.trimEnd(),e+=", "+anww_localized.opens_a_new_window):e+=anww_localized.opens_a_new_window,n.attr("aria-label",e)}function d(e){e.mousemove((function(e){n.css({top:e.pageY+10+"px",left:e.pageX+10+"px"})})).hover((function(){n.show().html(anww_localized.opens_a_new_window)}),(function(){n.hide()})),e.on({focusin:function(){var t=e.offset();n.css({top:t.top+e.outerHeight()+"px",left:t.left+"px"}),n.show().html(anww_localized.opens_a_new_window)},focusout:function(){n.hide()}})}}))}$(window).on("load",(function(){n=$("<div/>").css({position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none"}).addClass("anww-tooltip").appendTo("body"),e()})),$(document).on("facetwp-loaded",e)}(jQuery);1 !function(){let e,t;const n=()=>{document.querySelectorAll(".anww-external-link-icon").forEach((e=>e.remove())),document.querySelectorAll("a").forEach((e=>{let t=!1;const n=e.getAttribute("onclick");if("_blank"===e.getAttribute("target")&&(o(e),i(e),a(e),t=!0),n&&n.includes("window.open")){const d=n.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),l=d?d[1]:"";"_blank"!==l&&""!==l||(o(e),i(e),a(e),t=!0)}}))},o=e=>{const t=document.createElement("i");t.classList.add("anww-external-link-icon"),t.setAttribute("aria-hidden","true"),e.appendChild(t)},i=e=>{let t="";e.hasAttribute("aria-label")?t=e.getAttribute("aria-label"):e.querySelector("img")?t=e.querySelector("img").getAttribute("alt"):e.textContent&&(t=e.textContent.trim()),t=t?`${t}, ${anww_localized.opens_a_new_window}`:anww_localized.opens_a_new_window,e.setAttribute("aria-label",t)},a=e=>{e.addEventListener("mouseenter",(t=>{d(e,t.pageX,t.pageY)})),e.addEventListener("focusin",(t=>{const n=e.getBoundingClientRect();d(e,n.left+window.scrollX,n.top+n.height+window.scrollY)})),e.addEventListener("mouseleave",l),e.addEventListener("focusout",l)},d=(n,o,i)=>{clearTimeout(t),e.textContent=anww_localized.opens_a_new_window,e.style.display="block";const a=e.offsetWidth,d=e.offsetHeight;o+a+10>window.innerWidth&&(o-=a+20),i+d+10>window.innerHeight+window.scrollY&&(i-=d+20),e.style.top=`${i+10}px`,e.style.left=`${o+10}px`},l=()=>{t=setTimeout(s,300)},s=()=>{e.style.display="none"};window.addEventListener("load",(()=>{e=document.createElement("div"),e.setAttribute("role","tooltip"),e.classList.add("anww-tooltip"),Object.assign(e.style,{position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none",pointerEvents:"auto",boxShadow:"0px 4px 6px rgba(0,0,0,0.1)",maxWidth:"200px",whiteSpace:"nowrap"}),document.body.appendChild(e),document.addEventListener("click",(e=>{e.target.closest(".anww-tooltip, a[target='_blank']")||s()})),document.addEventListener("keydown",(e=>{"Escape"===e.key&&s()})),e.addEventListener("mouseenter",(()=>{clearTimeout(t)})),e.addEventListener("mouseleave",(()=>{s()})),n()})),document.addEventListener("facetwp-loaded",n)}(); -
accessibility-new-window-warnings/tags/1.1.0/assets/js/accessibility-new-window-warnings.js
r3174459 r3248595 1 (function ($) { 2 "use strict"; 1 /* global anww_localized */ 3 2 4 var anww_link_tooltip; 3 (function () { 5 4 6 function initializeTooltip() { 7 anww_link_tooltip = $('<div/>').css({ 8 position: 'absolute', 9 background: 'white', 10 color: '#1e1e1e', 11 fontSize: '16px', 12 border: '1px solid black', 13 padding: '5px 10px', 5 let anwwLinkTooltip; 6 let tooltipTimeout; 7 8 /** 9 * Initializes the tooltip element and event listeners. 10 */ 11 const initializeTooltip = () => { 12 anwwLinkTooltip = document.createElement("div"); 13 anwwLinkTooltip.setAttribute("role", "tooltip"); 14 anwwLinkTooltip.classList.add("anww-tooltip"); 15 Object.assign(anwwLinkTooltip.style, { 16 position: "absolute", 17 background: "white", 18 color: "#1e1e1e", 19 fontSize: "16px", 20 border: "1px solid black", 21 padding: "5px 10px", 14 22 zIndex: 9999, 15 display: 'none' 16 }).addClass('anww-tooltip').appendTo('body'); 17 } 23 display: "none", 24 pointerEvents: "auto", 25 boxShadow: "0px 4px 6px rgba(0,0,0,0.1)", 26 maxWidth: "200px", 27 whiteSpace: "nowrap" 28 }); 29 document.body.appendChild(anwwLinkTooltip); 18 30 19 function processLinks() { 20 var anww_label = ''; 31 // Hide tooltip when clicking outside or pressing Escape 32 document.addEventListener("click", (event) => { 33 if (!event.target.closest(".anww-tooltip, a[target='_blank']")) { 34 hideTooltip(); 35 } 36 }); 21 37 38 document.addEventListener("keydown", (event) => { 39 if (event.key === "Escape") { 40 hideTooltip(); 41 } 42 }); 43 44 // Keep tooltip visible when hovered 45 anwwLinkTooltip.addEventListener("mouseenter", () => { 46 clearTimeout(tooltipTimeout); 47 }); 48 49 anwwLinkTooltip.addEventListener("mouseleave", () => { 50 hideTooltip(); 51 }); 52 }; 53 54 /** 55 * Processes all anchor links and applies necessary accessibility enhancements. 56 */ 57 const processLinks = () => { 22 58 // Remove previously appended icons to avoid duplication 23 $(".anww-external-link-icon").remove();59 document.querySelectorAll(".anww-external-link-icon").forEach(icon => icon.remove()); 24 60 25 $("a").each(function (){26 varhasIcon = false;27 var onclickAttr = $(this).attr("onclick");61 document.querySelectorAll("a").forEach((link) => { 62 let hasIcon = false; 63 const onclickAttr = link.getAttribute("onclick"); 28 64 29 65 // Check if the link opens a new window using target="_blank" 30 if ( $(this).attr("target") === "_blank") {31 addExternalLinkIcon( $(this));32 updateAriaLabel( $(this));33 addTooltipHandlers( $(this));66 if (link.getAttribute("target") === "_blank") { 67 addExternalLinkIcon(link); 68 updateAriaLabel(link); 69 addTooltipHandlers(link); 34 70 hasIcon = true; 35 71 } … … 37 73 // Check if the link uses window.open in the onclick attribute 38 74 if (onclickAttr && onclickAttr.includes("window.open")) { 39 // Extract window.open arguments 40 var windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/); 41 var targetWindow = windowOpenMatch ? windowOpenMatch[1] : ''; 75 const windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/); 76 const targetWindow = windowOpenMatch ? windowOpenMatch[1] : ""; 42 77 43 // Ensure window.open is opening a new window (i.e., '_blank') 44 if (targetWindow === '_blank' || targetWindow === '') { 45 addExternalLinkIcon($(this)); 46 updateAriaLabel($(this)); 47 addTooltipHandlers($(this)); 78 if (targetWindow === "_blank" || targetWindow === "") { 79 addExternalLinkIcon(link); 80 updateAriaLabel(link); 81 addTooltipHandlers(link); 48 82 hasIcon = true; 49 83 } 50 84 } 51 85 52 // If no external link behavior detected, do not proceed with tooltip 53 if (!hasIcon) { 54 return; 55 } 86 if (!hasIcon) return; 87 }); 88 }; 56 89 57 function addExternalLinkIcon(link) {58 // Add icon to link59 if ($(':header', link).length) {60 $(':header', link).append('<i class="anww-external-link-icon" aria-hidden="true"></i>');61 } else{62 link.append('<i class="anww-external-link-icon" aria-hidden="true"></i>');63 }64 }90 /** 91 * Adds an external link icon to the specified link. 92 * @param {HTMLElement} link - The link element to modify. 93 */ 94 const addExternalLinkIcon = (link) => { 95 const icon = document.createElement("i"); 96 icon.classList.add("anww-external-link-icon"); 97 icon.setAttribute("aria-hidden", "true"); 65 98 66 function updateAriaLabel(link) { 67 // Get aria label text 68 if (link.attr("aria-label")) { 69 anww_label = link.attr("aria-label"); 70 } else if ($('img', link).length) { 71 anww_label = link.find("img").attr("alt"); 72 } else if (link.text()) { 73 anww_label = link.text(); 74 } 99 link.appendChild(icon); 100 }; 75 101 76 // Add warning label 77 if (anww_label) { 78 anww_label = anww_label.trimEnd(); 79 anww_label += ", " + anww_localized.opens_a_new_window; 80 } else { 81 anww_label += anww_localized.opens_a_new_window; 82 } 102 /** 103 * Updates the aria-label of the specified link. 104 * @param {HTMLElement} link - The link element to modify. 105 */ 106 const updateAriaLabel = (link) => { 107 let anwwLabel = ""; 83 108 84 // Add aria-label to link 85 link.attr("aria-label", anww_label); 86 } 109 if (link.hasAttribute("aria-label")) { 110 anwwLabel = link.getAttribute("aria-label"); 111 } else if (link.querySelector("img")) { 112 anwwLabel = link.querySelector("img").getAttribute("alt"); 113 } else if (link.textContent) { 114 anwwLabel = link.textContent.trim(); 115 } 87 116 88 function addTooltipHandlers(link) { 89 // Position and show link_tooltip on hover 90 link.mousemove(function (e) { 91 anww_link_tooltip.css({ 92 top: e.pageY + 10 + 'px', 93 left: e.pageX + 10 + 'px' 94 }); 95 }) 96 .hover(function () { 97 anww_link_tooltip.show().html(anww_localized.opens_a_new_window); 98 }, function () { 99 anww_link_tooltip.hide(); 100 }); 117 anwwLabel = anwwLabel ? `${anwwLabel}, ${anww_localized.opens_a_new_window}` : anww_localized.opens_a_new_window; 118 link.setAttribute("aria-label", anwwLabel); 119 }; 101 120 102 // Position and show link_tooltip on focus103 link.on({104 focusin: function () {105 var position = link.offset();106 anww_link_tooltip.css({107 top: position.top + link.outerHeight() + 'px',108 left: position.left + 'px'109 });121 /** 122 * Adds tooltip event handlers to the specified link. 123 * @param {HTMLElement} link - The link element to modify. 124 */ 125 const addTooltipHandlers = (link) => { 126 link.addEventListener("mouseenter", (e) => { 127 showTooltip(link, e.pageX, e.pageY); 128 }); 110 129 111 anww_link_tooltip.show().html(anww_localized.opens_a_new_window); 112 }, 113 focusout: function () { 114 anww_link_tooltip.hide(); 115 } 116 }); 117 } 130 link.addEventListener("focusin", (e) => { 131 const rect = link.getBoundingClientRect(); 132 showTooltip(link, rect.left + window.scrollX, rect.top + rect.height + window.scrollY); 118 133 }); 119 }120 134 121 $(window).on('load', function () { 135 link.addEventListener("mouseleave", hideTooltipWithDelay); 136 link.addEventListener("focusout", hideTooltipWithDelay); 137 }; 138 139 /** 140 * Displays the tooltip near the specified coordinates, adjusting if it overflows. 141 * @param {HTMLElement} link - The link triggering the tooltip. 142 * @param {number} x - The x-coordinate. 143 * @param {number} y - The y-coordinate. 144 */ 145 const showTooltip = (link, x, y) => { 146 clearTimeout(tooltipTimeout); 147 148 anwwLinkTooltip.textContent = anww_localized.opens_a_new_window; 149 anwwLinkTooltip.style.display = "block"; 150 151 const tooltipWidth = anwwLinkTooltip.offsetWidth; 152 const tooltipHeight = anwwLinkTooltip.offsetHeight; 153 const windowWidth = window.innerWidth; 154 const windowHeight = window.innerHeight; 155 const scrollTop = window.scrollY; 156 157 // Adjust X if the tooltip overflows the right edge 158 if (x + tooltipWidth + 10 > windowWidth) { 159 x -= (tooltipWidth + 20); 160 } 161 162 // Adjust Y if the tooltip overflows the bottom edge 163 if (y + tooltipHeight + 10 > windowHeight + scrollTop) { 164 y -= (tooltipHeight + 20); 165 } 166 167 anwwLinkTooltip.style.top = `${y + 10}px`; 168 anwwLinkTooltip.style.left = `${x + 10}px`; 169 }; 170 171 /** 172 * Delays hiding the tooltip to prevent flickering. 173 */ 174 const hideTooltipWithDelay = () => { 175 tooltipTimeout = setTimeout(hideTooltip, 300); 176 }; 177 178 /** 179 * Hides the tooltip. 180 */ 181 const hideTooltip = () => { 182 anwwLinkTooltip.style.display = "none"; 183 }; 184 185 // Initialize tooltip and process links on page load 186 window.addEventListener("load", () => { 122 187 initializeTooltip(); 123 188 processLinks(); … … 125 190 126 191 // Support for FacetWP: Re-run the processLinks function when FacetWP refreshes the page 127 $(document).on('facetwp-loaded', processLinks); 128 129 })(jQuery); 192 document.addEventListener("facetwp-loaded", processLinks); 193 })(); -
accessibility-new-window-warnings/trunk/README.txt
r3174459 r3248595 3 3 Tags: accessibility, accessible, wcag, ada, a11y, section 508, links, open new window, open new tab 4 4 Requires at least: 6.4.0 5 Tested up to: 6. 6.26 Stable tag: 1. 0.105 Tested up to: 6.7.2 6 Stable tag: 1.1.0 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 75 75 == Changelog == 76 76 77 = 1.1.0 = 78 * Fixed - tooltips can now be dismissed using the Escape key, ensuring better accessibility 79 * Fixed - tooltip content is now hoverable, preventing accidental dismissal 80 * Refactored - code for better performance and maintainability using modern JavaScript practices 81 77 82 = 1.0.10 = 78 83 * Added - detection and processing of window.open() links with consistent tooltip/ARIA label handling for both target="_blank" and window.open() -
accessibility-new-window-warnings/trunk/accessibility-new-window-warnings.php
r3174459 r3248595 4 4 * Plugin URI: https://a11ychecker.com 5 5 * Description: Make links that open in a new window accessible by adding a warning. 6 * Version: 1. 0.106 * Version: 1.1.0 7 7 * Author: Equalize Digital 8 8 * Author URI: https://equalizedigital.com … … 14 14 */ 15 15 16 define( 'ANWW_VERSION', '1. 0.10' );16 define( 'ANWW_VERSION', '1.1.0' ); 17 17 define( 'ANWW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 18 18 define( 'ANWW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); -
accessibility-new-window-warnings/trunk/assets/js/accessibility-new-window-warnings-min.js
r3174459 r3248595 1 !function( $){"use strict";var n;function e(){var e="";$(".anww-external-link-icon").remove(),$("a").each((function(){var t=$(this).attr("onclick");if("_blank"===$(this).attr("target")&&(o($(this)),l($(this)),d($(this)),!0),t&&t.includes("window.open")){var a=t.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),i=a?a[1]:"";"_blank"!==i&&""!==i||(o($(this)),l($(this)),d($(this)),!0)}function o(n){$(":header",n).length?$(":header",n).append('<i class="anww-external-link-icon" aria-hidden="true"></i>'):n.append('<i class="anww-external-link-icon" aria-hidden="true"></i>')}function l(n){n.attr("aria-label")?e=n.attr("aria-label"):$("img",n).length?e=n.find("img").attr("alt"):n.text()&&(e=n.text()),e?(e=e.trimEnd(),e+=", "+anww_localized.opens_a_new_window):e+=anww_localized.opens_a_new_window,n.attr("aria-label",e)}function d(e){e.mousemove((function(e){n.css({top:e.pageY+10+"px",left:e.pageX+10+"px"})})).hover((function(){n.show().html(anww_localized.opens_a_new_window)}),(function(){n.hide()})),e.on({focusin:function(){var t=e.offset();n.css({top:t.top+e.outerHeight()+"px",left:t.left+"px"}),n.show().html(anww_localized.opens_a_new_window)},focusout:function(){n.hide()}})}}))}$(window).on("load",(function(){n=$("<div/>").css({position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none"}).addClass("anww-tooltip").appendTo("body"),e()})),$(document).on("facetwp-loaded",e)}(jQuery);1 !function(){let e,t;const n=()=>{document.querySelectorAll(".anww-external-link-icon").forEach((e=>e.remove())),document.querySelectorAll("a").forEach((e=>{let t=!1;const n=e.getAttribute("onclick");if("_blank"===e.getAttribute("target")&&(o(e),i(e),a(e),t=!0),n&&n.includes("window.open")){const d=n.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/),l=d?d[1]:"";"_blank"!==l&&""!==l||(o(e),i(e),a(e),t=!0)}}))},o=e=>{const t=document.createElement("i");t.classList.add("anww-external-link-icon"),t.setAttribute("aria-hidden","true"),e.appendChild(t)},i=e=>{let t="";e.hasAttribute("aria-label")?t=e.getAttribute("aria-label"):e.querySelector("img")?t=e.querySelector("img").getAttribute("alt"):e.textContent&&(t=e.textContent.trim()),t=t?`${t}, ${anww_localized.opens_a_new_window}`:anww_localized.opens_a_new_window,e.setAttribute("aria-label",t)},a=e=>{e.addEventListener("mouseenter",(t=>{d(e,t.pageX,t.pageY)})),e.addEventListener("focusin",(t=>{const n=e.getBoundingClientRect();d(e,n.left+window.scrollX,n.top+n.height+window.scrollY)})),e.addEventListener("mouseleave",l),e.addEventListener("focusout",l)},d=(n,o,i)=>{clearTimeout(t),e.textContent=anww_localized.opens_a_new_window,e.style.display="block";const a=e.offsetWidth,d=e.offsetHeight;o+a+10>window.innerWidth&&(o-=a+20),i+d+10>window.innerHeight+window.scrollY&&(i-=d+20),e.style.top=`${i+10}px`,e.style.left=`${o+10}px`},l=()=>{t=setTimeout(s,300)},s=()=>{e.style.display="none"};window.addEventListener("load",(()=>{e=document.createElement("div"),e.setAttribute("role","tooltip"),e.classList.add("anww-tooltip"),Object.assign(e.style,{position:"absolute",background:"white",color:"#1e1e1e",fontSize:"16px",border:"1px solid black",padding:"5px 10px",zIndex:9999,display:"none",pointerEvents:"auto",boxShadow:"0px 4px 6px rgba(0,0,0,0.1)",maxWidth:"200px",whiteSpace:"nowrap"}),document.body.appendChild(e),document.addEventListener("click",(e=>{e.target.closest(".anww-tooltip, a[target='_blank']")||s()})),document.addEventListener("keydown",(e=>{"Escape"===e.key&&s()})),e.addEventListener("mouseenter",(()=>{clearTimeout(t)})),e.addEventListener("mouseleave",(()=>{s()})),n()})),document.addEventListener("facetwp-loaded",n)}(); -
accessibility-new-window-warnings/trunk/assets/js/accessibility-new-window-warnings.js
r3174459 r3248595 1 (function ($) { 2 "use strict"; 1 /* global anww_localized */ 3 2 4 var anww_link_tooltip; 3 (function () { 5 4 6 function initializeTooltip() { 7 anww_link_tooltip = $('<div/>').css({ 8 position: 'absolute', 9 background: 'white', 10 color: '#1e1e1e', 11 fontSize: '16px', 12 border: '1px solid black', 13 padding: '5px 10px', 5 let anwwLinkTooltip; 6 let tooltipTimeout; 7 8 /** 9 * Initializes the tooltip element and event listeners. 10 */ 11 const initializeTooltip = () => { 12 anwwLinkTooltip = document.createElement("div"); 13 anwwLinkTooltip.setAttribute("role", "tooltip"); 14 anwwLinkTooltip.classList.add("anww-tooltip"); 15 Object.assign(anwwLinkTooltip.style, { 16 position: "absolute", 17 background: "white", 18 color: "#1e1e1e", 19 fontSize: "16px", 20 border: "1px solid black", 21 padding: "5px 10px", 14 22 zIndex: 9999, 15 display: 'none' 16 }).addClass('anww-tooltip').appendTo('body'); 17 } 23 display: "none", 24 pointerEvents: "auto", 25 boxShadow: "0px 4px 6px rgba(0,0,0,0.1)", 26 maxWidth: "200px", 27 whiteSpace: "nowrap" 28 }); 29 document.body.appendChild(anwwLinkTooltip); 18 30 19 function processLinks() { 20 var anww_label = ''; 31 // Hide tooltip when clicking outside or pressing Escape 32 document.addEventListener("click", (event) => { 33 if (!event.target.closest(".anww-tooltip, a[target='_blank']")) { 34 hideTooltip(); 35 } 36 }); 21 37 38 document.addEventListener("keydown", (event) => { 39 if (event.key === "Escape") { 40 hideTooltip(); 41 } 42 }); 43 44 // Keep tooltip visible when hovered 45 anwwLinkTooltip.addEventListener("mouseenter", () => { 46 clearTimeout(tooltipTimeout); 47 }); 48 49 anwwLinkTooltip.addEventListener("mouseleave", () => { 50 hideTooltip(); 51 }); 52 }; 53 54 /** 55 * Processes all anchor links and applies necessary accessibility enhancements. 56 */ 57 const processLinks = () => { 22 58 // Remove previously appended icons to avoid duplication 23 $(".anww-external-link-icon").remove();59 document.querySelectorAll(".anww-external-link-icon").forEach(icon => icon.remove()); 24 60 25 $("a").each(function (){26 varhasIcon = false;27 var onclickAttr = $(this).attr("onclick");61 document.querySelectorAll("a").forEach((link) => { 62 let hasIcon = false; 63 const onclickAttr = link.getAttribute("onclick"); 28 64 29 65 // Check if the link opens a new window using target="_blank" 30 if ( $(this).attr("target") === "_blank") {31 addExternalLinkIcon( $(this));32 updateAriaLabel( $(this));33 addTooltipHandlers( $(this));66 if (link.getAttribute("target") === "_blank") { 67 addExternalLinkIcon(link); 68 updateAriaLabel(link); 69 addTooltipHandlers(link); 34 70 hasIcon = true; 35 71 } … … 37 73 // Check if the link uses window.open in the onclick attribute 38 74 if (onclickAttr && onclickAttr.includes("window.open")) { 39 // Extract window.open arguments 40 var windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/); 41 var targetWindow = windowOpenMatch ? windowOpenMatch[1] : ''; 75 const windowOpenMatch = onclickAttr.match(/window\.open\([^,]+,\s*['"]([^'"]+)['"]/); 76 const targetWindow = windowOpenMatch ? windowOpenMatch[1] : ""; 42 77 43 // Ensure window.open is opening a new window (i.e., '_blank') 44 if (targetWindow === '_blank' || targetWindow === '') { 45 addExternalLinkIcon($(this)); 46 updateAriaLabel($(this)); 47 addTooltipHandlers($(this)); 78 if (targetWindow === "_blank" || targetWindow === "") { 79 addExternalLinkIcon(link); 80 updateAriaLabel(link); 81 addTooltipHandlers(link); 48 82 hasIcon = true; 49 83 } 50 84 } 51 85 52 // If no external link behavior detected, do not proceed with tooltip 53 if (!hasIcon) { 54 return; 55 } 86 if (!hasIcon) return; 87 }); 88 }; 56 89 57 function addExternalLinkIcon(link) {58 // Add icon to link59 if ($(':header', link).length) {60 $(':header', link).append('<i class="anww-external-link-icon" aria-hidden="true"></i>');61 } else{62 link.append('<i class="anww-external-link-icon" aria-hidden="true"></i>');63 }64 }90 /** 91 * Adds an external link icon to the specified link. 92 * @param {HTMLElement} link - The link element to modify. 93 */ 94 const addExternalLinkIcon = (link) => { 95 const icon = document.createElement("i"); 96 icon.classList.add("anww-external-link-icon"); 97 icon.setAttribute("aria-hidden", "true"); 65 98 66 function updateAriaLabel(link) { 67 // Get aria label text 68 if (link.attr("aria-label")) { 69 anww_label = link.attr("aria-label"); 70 } else if ($('img', link).length) { 71 anww_label = link.find("img").attr("alt"); 72 } else if (link.text()) { 73 anww_label = link.text(); 74 } 99 link.appendChild(icon); 100 }; 75 101 76 // Add warning label 77 if (anww_label) { 78 anww_label = anww_label.trimEnd(); 79 anww_label += ", " + anww_localized.opens_a_new_window; 80 } else { 81 anww_label += anww_localized.opens_a_new_window; 82 } 102 /** 103 * Updates the aria-label of the specified link. 104 * @param {HTMLElement} link - The link element to modify. 105 */ 106 const updateAriaLabel = (link) => { 107 let anwwLabel = ""; 83 108 84 // Add aria-label to link 85 link.attr("aria-label", anww_label); 86 } 109 if (link.hasAttribute("aria-label")) { 110 anwwLabel = link.getAttribute("aria-label"); 111 } else if (link.querySelector("img")) { 112 anwwLabel = link.querySelector("img").getAttribute("alt"); 113 } else if (link.textContent) { 114 anwwLabel = link.textContent.trim(); 115 } 87 116 88 function addTooltipHandlers(link) { 89 // Position and show link_tooltip on hover 90 link.mousemove(function (e) { 91 anww_link_tooltip.css({ 92 top: e.pageY + 10 + 'px', 93 left: e.pageX + 10 + 'px' 94 }); 95 }) 96 .hover(function () { 97 anww_link_tooltip.show().html(anww_localized.opens_a_new_window); 98 }, function () { 99 anww_link_tooltip.hide(); 100 }); 117 anwwLabel = anwwLabel ? `${anwwLabel}, ${anww_localized.opens_a_new_window}` : anww_localized.opens_a_new_window; 118 link.setAttribute("aria-label", anwwLabel); 119 }; 101 120 102 // Position and show link_tooltip on focus103 link.on({104 focusin: function () {105 var position = link.offset();106 anww_link_tooltip.css({107 top: position.top + link.outerHeight() + 'px',108 left: position.left + 'px'109 });121 /** 122 * Adds tooltip event handlers to the specified link. 123 * @param {HTMLElement} link - The link element to modify. 124 */ 125 const addTooltipHandlers = (link) => { 126 link.addEventListener("mouseenter", (e) => { 127 showTooltip(link, e.pageX, e.pageY); 128 }); 110 129 111 anww_link_tooltip.show().html(anww_localized.opens_a_new_window); 112 }, 113 focusout: function () { 114 anww_link_tooltip.hide(); 115 } 116 }); 117 } 130 link.addEventListener("focusin", (e) => { 131 const rect = link.getBoundingClientRect(); 132 showTooltip(link, rect.left + window.scrollX, rect.top + rect.height + window.scrollY); 118 133 }); 119 }120 134 121 $(window).on('load', function () { 135 link.addEventListener("mouseleave", hideTooltipWithDelay); 136 link.addEventListener("focusout", hideTooltipWithDelay); 137 }; 138 139 /** 140 * Displays the tooltip near the specified coordinates, adjusting if it overflows. 141 * @param {HTMLElement} link - The link triggering the tooltip. 142 * @param {number} x - The x-coordinate. 143 * @param {number} y - The y-coordinate. 144 */ 145 const showTooltip = (link, x, y) => { 146 clearTimeout(tooltipTimeout); 147 148 anwwLinkTooltip.textContent = anww_localized.opens_a_new_window; 149 anwwLinkTooltip.style.display = "block"; 150 151 const tooltipWidth = anwwLinkTooltip.offsetWidth; 152 const tooltipHeight = anwwLinkTooltip.offsetHeight; 153 const windowWidth = window.innerWidth; 154 const windowHeight = window.innerHeight; 155 const scrollTop = window.scrollY; 156 157 // Adjust X if the tooltip overflows the right edge 158 if (x + tooltipWidth + 10 > windowWidth) { 159 x -= (tooltipWidth + 20); 160 } 161 162 // Adjust Y if the tooltip overflows the bottom edge 163 if (y + tooltipHeight + 10 > windowHeight + scrollTop) { 164 y -= (tooltipHeight + 20); 165 } 166 167 anwwLinkTooltip.style.top = `${y + 10}px`; 168 anwwLinkTooltip.style.left = `${x + 10}px`; 169 }; 170 171 /** 172 * Delays hiding the tooltip to prevent flickering. 173 */ 174 const hideTooltipWithDelay = () => { 175 tooltipTimeout = setTimeout(hideTooltip, 300); 176 }; 177 178 /** 179 * Hides the tooltip. 180 */ 181 const hideTooltip = () => { 182 anwwLinkTooltip.style.display = "none"; 183 }; 184 185 // Initialize tooltip and process links on page load 186 window.addEventListener("load", () => { 122 187 initializeTooltip(); 123 188 processLinks(); … … 125 190 126 191 // Support for FacetWP: Re-run the processLinks function when FacetWP refreshes the page 127 $(document).on('facetwp-loaded', processLinks); 128 129 })(jQuery); 192 document.addEventListener("facetwp-loaded", processLinks); 193 })();
Note: See TracChangeset
for help on using the changeset viewer.