For a WordPress site I needed an external link solution that opens external links in a new tab and adds an icon next to links. WordPress has some plugins that do this but there’s something about each one I don’t like. Or, some have PHP warnings/errors when installing. So, here’s a pure JavaScript solution that I feel is pretty robust yet lightweight.
Since everyone’s setup is different, you can list patterns (that would be in a URL) for the script to ignore. For example, you will see within the code that it currently ignores inline JavaScript Void, and some image extensions:
const ignoredLinks = ['javascript:void(0)', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
It is also optimized for SEO thanks to the nofollow attribute, and for screen readers, it will tell the user that this link opens in a new window.
(function() { 'use strict'; // Define the SVG icon const externalLinkIcon = ` <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path> <polyline points="15 3 21 3 21 9"></polyline> <line x1="10" y1="14" x2="21" y2="3"></line> </svg> `; // Get the current hostname const currentHostname = window.location.hostname; // List of patterns or URLs to ignore (add your patterns here, separated by commas) const ignoredLinks = ['javascript:void(0)', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']; // Function to check if a URL should be ignored function isIgnoredLink(url) { for (const pattern of ignoredLinks) { if (url.includes(pattern)) { return true; } } return false; } // Function to check if a URL is external function isExternalLink(url) { const parser = document.createElement('a'); parser.href = url; // Check if the link is a hash link (same page) if (parser.hash && parser.hostname === currentHostname) { return false; } // Check if the link is external and not ignored return parser.hostname !== currentHostname && !isIgnoredLink(url); } // Function to handle external links function handleExternalLink(event) { const link = event.target.closest('.post a:not([href^="#"]),.page a:not([href^="#"]),.single a:not([href^="#"]),.archive a:not([href^="#"])'); if (link && isExternalLink(link.href)) { event.preventDefault(); link.setAttribute('rel', 'noopener noreferrer'); link.setAttribute('target', '_blank'); window.open(link.href, '_blank'); } } // Attach the event listener const contentAreas = document.querySelectorAll('.post, .page, .single, .archive'); contentAreas.forEach(area => area.addEventListener('click', handleExternalLink, false)); // Use a MutationObserver to handle dynamically added links const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { const externalLinks = node.querySelectorAll('.post a:not([href^="#"]):not([rel="noopener noreferrer"]),.page a:not([href^="#"]):not([rel="noopener noreferrer"]),.single a:not([href^="#"]):not([rel="noopener noreferrer"]),.archive a:not([href^="#"]):not([rel="noopener noreferrer"])'); if (externalLinks.length > 0) { externalLinks.forEach((link) => { if (isExternalLink(link.href)) { link.setAttribute('rel', 'noopener noreferrer'); link.setAttribute('target', '_blank'); const externalIcon = document.createElement('span'); externalIcon.innerHTML = externalLinkIcon; externalIcon.classList.add('external-link-icon'); externalIcon.setAttribute('aria-label', 'Opens in new tab'); externalIcon.setAttribute('title', 'Opens in new tab'); link.insertAdjacentElement('afterend', externalIcon); } }); } } }); }); }); contentAreas.forEach(area => observer.observe(area, { childList: true, subtree: true })); })();
Leave a Reply