4

According to the spec, language is inherited from the nearest ancestor element which has lang set. So how do I get the language of one of these descendents?

I'm setting the language with the lang attribute:

<div id="outer" lang="es">
    <div id="inner">¿Por qué?</div>
</div>

And trying to access it with the lang property:

alert(document.getElementById('outer').lang);
alert(document.getElementById('inner').lang);

I pretty quickly noticed that the lang property of the inner div isn't the inherited language (or it's not inheriting; no idea). This fiddle demonstrates the behavior I'm seeing.

How can I determine an element's language with Javascript? I'm really looking to take advantage of the inheritance of the lang attribute, as I also need this to work with screen-readers.

Andrew Vermie
  • 623
  • 3
  • 8

2 Answers2

3

From the very spec you linked to:

To determine the language of a node, user agents must look at the nearest ancestor element (including the element itself if the node is an element) that has a lang attribute in the XML namespace set or is an HTML element and has a lang in no namespace attribute set. That attribute specifies the language of the node (regardless of its value).

Inheriting from another element doesn't mean the attribute is set for the element itself. Therefore, your code must, as well, find the nearest parent with the lang attribute (if any exist). See this question if that sounds problematic.

Community
  • 1
  • 1
Etheryte
  • 24,589
  • 11
  • 71
  • 116
  • The OP needs to use [*hasAttribute*](http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttr) to determine if an element has the *lang* attribute. I guess `if (typeof element.getAttribute('lang') == 'string')` might do the job also. – RobG Feb 11 '15 at 20:17
  • 2
    I was really hoping that that *actual* user agent (the browser) would be able to do this for me. – Andrew Vermie Feb 11 '15 at 20:18
1

Here is a function that will return the value of the "lang" attribute of the nearest ancestor of any element, or undefined if none is found:

function elementLang(el) {
  while (el) {
    if (el.hasAttribute('lang')) {
      return el.getAttribute('lang');
    }
    el = el.parentElement;
  }
}

// From your example jsFiddle...
elementLang(document.getElementById('inner')); // => "es"
elementLang(document.getElementById('outer')); // => "es"
elementLang(document.getElementById('output')); // => undefined
maerics
  • 151,642
  • 46
  • 269
  • 291
  • This would be a better solution if you made it iterative instead of recursive. Recursion is generally slow and there's no need to use it here. – Etheryte Feb 11 '15 at 20:01
  • So iteration is better than recursion? – maerics Feb 11 '15 at 20:05
  • Simple iteration is generally certainly faster, you simply can't always use it to achieve what you want in a reasonable manner. – Etheryte Feb 11 '15 at 20:06
  • What about tail recursion on tail-call-optimizing interpreters or compilers? – maerics Feb 11 '15 at 20:07
  • See the second half of my comment. – Etheryte Feb 11 '15 at 20:08
  • This will fail where the *lang* attribute is an empty string, e.g. `
    `. Better to use [*hasAttribute*](http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-ElHasAttr).
    – RobG Feb 11 '15 at 20:26
  • @RobG: yes, good point; I've updated my code. (Although I'm not sure if the empty string is more useful than null for conveying language info.) – maerics Feb 11 '15 at 20:30
  • @maerics—you could just return *undefined* if no element has the attribute (i.e. remove the final return statement). There is no need for the *else* block as the *if* block returns if true (the assignment statement is still required, just not in an else block). – RobG Feb 11 '15 at 22:16