<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;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.