title: Inferring language attribute while traversing the dom
body: <p>I'm working on a site where there there are multiple language attributes declared throughout it. I'm trying to extract all of the text from each language, but sometimes the language is inferred from a parent element, so each element itself may not have the attribute. Sometimes, the element which set the language attribute can be many levels above the node. </p>
  
  <p>For example, if the website structure is like so (simplified):</p>
  
  <pre><code>&lt;html&gt;
  &lt;body lang="en"&gt;
    &lt;header lang="ko"&gt;
      &lt;span&gt;氏氏氏氏&lt;/span&gt;
      &lt;p lang="en"&gt;A whole new world&lt;/p&gt;
    &lt;/header&gt;
    &lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit&lt;/p&gt;
    &lt;p lang="cn"&gt;也称乱数假文或者哑元文本&lt;/p&gt;
    &lt;p&gt;Sed ut perspiciatis unde omnis iste natus error sit voluptatem&lt;/p&gt;
    &lt;footer lang="ja"&gt;
      &lt;ul&gt;
        &lt;li&gt;金金金金&lt;/li&gt;
        &lt;li&gt;金或金或&lt;/li&gt;
        &lt;li lang="ko"&gt;중중중중중&lt;/li&gt;
        &lt;li&gt;数假&lt;/li&gt;
      &lt;/ul&gt;
    &lt;footer&gt;
  &lt;/body&gt;
  &lt;/html&gt;
  &lt;/body&gt;
  </code></pre>
  
  <p>Getting all of the elements that have the <code>lang</code> attribute is not a good solution, because then the nodes without a <code>lang</code> tag are no longer included. I've tried creating a solution with this code, but it does not work well if the parent was set many previous elements above. Perhaps there is a different approach I could take? Or maybe there is an adjustment to this solution that would work well?</p>
  
  <pre><code>var findTextNodesWithLangAttribute = function(parentNode, selectedLanguage){
      var treeWalker = document.createTreeWalker(
          parentNode,
          // Only consider nodes that are text nodes (nodeType 3)
          NodeFilter.SHOW_TEXT,
          // Object containing the function to use for the acceptNode method
          // of the NodeFilter
          { acceptNode: function(node) {
                // Logic to determine whether to accept, reject or skip node
                // In this case, only accept nodes that have content
                // other than whitespace
                if ( ! /^\s*$/.test(node.data) ) {
                      // Test the language attribute
                      if(node.parentElement.hasAttribute("lang")) {
                          var nodeLang = node.parentElement.attributes["lang"].value;
                      } else if(node.parentNode.parentElement.hasAttribute("lang")) {
            var nodeLang = node.parentNode.parentElement.attributes["lang"].value;
          } else if(node.parentElement.parentElement.hasAttribute("lang")) {
            var nodeLang = node.parentElement.parentElement.attributes["lang"].value;
                      }; 
                    if(nodeLang == selectedLanguage) {
                          return NodeFilter.FILTER_ACCEPT;
                      }
                }
            }
          },
          false
      );
  
      var nodeList = [];
  
      while(treeWalker.nextNode()) nodeList.push(treeWalker.currentNode);
  
      return nodeList;
  }
  </code></pre>