<h1>Language of parts bookmarklet</h1>
<p>
Drag the "lang of parts" link to your Bookmarks bar to create a bookmarklet that when activated will <span lang="en">highlight</span> all the elements on the page with a lang attribute.</p>
<p>
<a href="javascript:(function() { function isVisible(element) { let currentElement = element; while (currentElement) { const style = window.getComputedStyle(currentElement); if (style.display === 'none' || style.visibility === 'hidden') { return false; } currentElement = currentElement.parentElement; }return true; } const elements = document.querySelectorAll('[lang]:not(html)'); const visibleElements = Array.from(elements).filter(isVisible); const count = elements.length; const invisibleCount = elements.length-visibleElements.length; const visibleCount = visibleElements.length; elements.forEach(element => { element.setAttribute('data-original-background', element.style.backgroundColor || ''); element.setAttribute('data-original-color', element.style.color || ''); element.setAttribute('data-original-background-image', element.style.backgroundImage || ''); element.style.backgroundColor = 'yellow'; element.style.color = 'black';element.style.backgroundImage = `url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0nMTAwJScgaGVpZ2h0PScxMDAlJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxyZWN0IHdpZHRoPScxMDAlJyBoZWlnaHQ9JzEwMCUnIGZpbGw9J25vbmUnIHN0cm9rZT0nIzMzMycgc3Ryb2tlLXdpZHRoPSc0JyBzdHJva2UtZGFzaGFycmF5PSc3JyBzdHJva2UtZGFzaG9mZnNldD0nMCcgc3Ryb2tlLWxpbmVjYXA9J3NxdWFyZScvPjwvc3ZnPg==)`; element.style.backgroundRepeat = 'no-repeat'; element.style.backgroundPosition = 'center'; element.style.backgroundSize = 'cover'; }); let message = document.createElement('div'); message.style.position = 'fixed'; message.style.top = '10px'; message.style.left = '50%'; message.style.transform = 'translateX(-50%)'; message.style.width = '360px'; message.style.backgroundColor = 'black'; message.style.color = 'yellow'; message.style.padding = '10px'; message.style.zIndex = '9999'; message.style.fontFamily = 'Arial, sans-serif'; message.style.fontSize = '14px'; message.style.textAlign = 'center'; message.style.border = '2px solid yellow'; message.style.cursor = 'move'; message.innerText = `Found ${count} body element(s) with a lang attribute`; let closeButton = document.createElement('span'); closeButton.innerText = '✖'; closeButton.style.float = 'right'; closeButton.style.cursor = 'pointer'; closeButton.style.marginLeft = '10px'; closeButton.style.color = 'white'; closeButton.onclick = function() { visibleElements.forEach(element => { element.style.backgroundColor = element.getAttribute('data-original-background'); element.style.color = element.getAttribute('data-original-color'); element.style.backgroundImage = element.getAttribute('data-original-background-image'); element.removeAttribute('data-original-background'); element.removeAttribute('data-original-color'); element.removeAttribute('data-original-background-image'); }); message.remove(); }; message.appendChild(closeButton); const htmlLang = document.documentElement.getAttribute('lang'); const htmlLangMsg = htmlLang ? `The <html> lang attribute is: ${htmlLang}` : %27The <html> element does not have a lang attribute.%27; let htmlLangD = document.createElement(%27div%27); htmlLangD.innerText = htmlLangMsg; message.appendChild(htmlLangD); if (invisibleCount > 0) { let hiddenCount = document.createElement(%27div%27); hiddenCount.innerText = `(${invisibleCount} hidden)`;message.appendChild(hiddenCount); } document.body.appendChild(message); let isDragging = false; let offsetX, offsetY; message.onmousedown = function(e) { isDragging = true; offsetX = e.clientX - message.offsetLeft; offsetY = e.clientY - message.offsetTop; message.style.cursor = %27grabbing%27; }; document.onmousemove = function(e) { if (isDragging) { message.style.left = (e.clientX - offsetX) + %27px%27; message.style.top = (e.clientY - offsetY) + %27px%27; } }; document.onmouseup = function() { isDragging = false; message.style.cursor = %27move%27; };})();">Lang of parts</a>
</p>
<p class="test">This is a test paragraph. Click the Lang of parts link to test the highlighting. We all like Swedish <span lang="sv">fika</span>. <span lang="sv" class="sr-only">Användare av skärmläsare får extra fika.</span></p>
<h2>What this bookmarklet does</h2>
<p>
It counts the total number of lang attributes in the body of the page (it doesn't include the lang attribute of the html element in the total).</p>
<p>If a lang attribute is present on the html element, it will tell you what language it is. It will also say if this attribute is missing.</p>
<p>If any hidden elements with lang attributes are found, it will tell you how many.</p>
<p>NOTE: depending on the element where a lang attribute is attached, the highlighting might not be visible. For example, the lang attribute has been placed on a div but a child div has a background and its size equals the parent div.
</p>
<p>This bookmarklet has been created to help when testing <a href="https://w3c.github.io/wcag/understanding/language-of-parts">WCAG 2.2 Success Criterion 3.1.2 Language of parts</a> (Level AA) as well as <a href="https://w3c.github.io/wcag/understanding/language-of-page">3.1.1 Language of page</a> (Level A).
</p>
<p>Created by <a href="https://beantin.net/about-james-royal-lawson/">James Royal-Lawson</a>, Beantin, October 2024.</p>
body {
font-family: 'Atkinson Hyperlegible', sans-serif;
line-height: 1.4;
/* line-height: 1.5; */
/* letter-spacing: 0.12em; */
/* word-spacing: 0.16em; */
margin: 3vh;
}
p {
max-width: 50ch;
margin-left: 1vh;
margin-right: 1vh;
}
.test {
border: 1px gray solid;
padding: 8px;
}
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.