title: XPath to find nodes with text + all their descendants & siblings that match certain criteria
body: <p><strong>Background:</strong><br>
  I'm trying to improve on a <a href="http://userscripts.org/scripts/show/13520" rel="nofollow noreferrer">Greasemonkey script</a> I found.<br>
  The script marks prices in foreign currencies and can translate them into the currency of your choice.</p>
  
  <p><strong>The main problem:</strong><br>
  How to make the script handle when prices are listed with tags, such as:</p>
  
  <pre><code>&lt;b&gt;&lt;i&gt;9.&lt;/i&gt;&lt;/b&gt;&lt;sup&gt;95&lt;/sup&gt;EUR
  </code></pre>
  
  <p>(Newegg.com does this, for example - they write their prices like so: &lt;span&gt;$&lt;/span&gt;174&lt;sup&gt;.99&lt;/sup&gt;).</p>
  
  <p>Currently, the script only finds prices that are listed in the same text node since the XPath expression being used is:</p>
  
  <pre><code>document.evaluate("//text()", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null)
  </code></pre>
  
  <p>Since the script needs to be fast, I'm trying to avoid stepping through the DOM too much...<br>
  <strong>Are there any XPath gurus who could help out with some smart solutions for this purpose?</strong>
  <br />  <br /><br>
  <strong>More detailed description of the problem:</strong><br>
  The code I now have for finding the text nodes:</p>
  
  <pre><code>var re_skip = /^(SCRIPT|IFRAME|TEXTAREA|STYLE|OPTION|TITLE|HEAD|NOSCRIPT)$/;  // List of elements whose text node-children can be skipped
  text = document.evaluate("//text()", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  var i = text.snapshotLength;
  while (i--) {
      el = text.snapshotItem(i);
      if (!el.parentNode || re_skip.test(el.parentNode.nodeName.toUpperCase()) || el.parentNode.className == 'autocurrency') {
          continue;
      }
  //  ...
  //  (RegEx logic to check if prices can be found in the text)
  }
  </code></pre>
  
  <p><br /></p>
  
  <ul>
  <li><p>The check to discard text nodes whose parent elements are listed in "re_skip" could be done in the XPath expression as well (<a href="https://stackoverflow.com/questions/3657146/how-do-i-retrieve-all-text-in-an-html-dom-but-exclude-script-and-style-tags">using the "not" notation</a>), right? And this would give a speed-increase? </p></li>
  <li><p>If an ordered XPath type is used instead, I guess I no longer will have to include a check to see if the parent of the text node being parsed is &lt;span class="autocurrency"&gt; (that is, the &lt;span&gt; that the script adds around matched prices).</p></li>
  <li><p>If I've understood things correctly, normalize-space() (as suggested <a href="https://stackoverflow.com/questions/6812465/xpath-search-through-html-tags">here</a>), cannot be used in this case, since the script adds a &lt;span class="autocurrency"&gt; around the matched amount and we need to retain the correct index for where this &lt;span&gt; should be entered.</p></li>
  <li><p>Is there a way for the XPath to allow only certain (inline) elements to be used in-between the currency values? Or perhaps it could do this: <strong>"when a node containing text is found, also include all of its children (and their children and so on) in the match - <em>unless</em> the child node is a block type element."  (or perhaps it should read: "...unless the child node is a DIV, P, TABLE, or any of the elements in re_skip")</strong></p></li>
  </ul>
  
  <p>I can re-write the regex to handle text such as "&lt;span&gt;$&lt;/span&gt;174&lt;sup&gt;.99&lt;/sup&gt;" as long as I <em>find</em> these text strings - preferably using XPath, as I have understood this to be much faster than stepping through the DOM.</p>
  
  <p>Thank you very much in advance for any help you can give me with this!<br>
   <br />
  <strong>--------------------------------------------------------------</strong><br>
  <strong>EDIT:</strong><br>
  OK, I realize now that the question could do with some clarification and some examples, so here they come.  A web page might look something like this:</p>
  
  <pre><code>&lt;body&gt;
    &lt;div&gt;
      &lt;span&gt;9.95 &lt;span&gt;EUR&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
      &lt;span&gt;8.&lt;sup&gt;95&lt;/sup&gt;&lt;/span&gt;AU$&lt;br /&gt;
      &lt;table&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;Bla&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
          &lt;tr&gt;
            &lt;td&gt;&lt;b&gt;7&lt;/b&gt;.95kr&lt;/td&gt;
          &lt;/tr&gt;
        &lt;/tbody&gt;
      &lt;/table&gt;
      &lt;div&gt;Bla bla&lt;/div&gt;
      6.95 &lt;span&gt;GBP&lt;/span&gt;
    &lt;/div&gt;
    &lt;div&gt;&lt;img src="" /&gt;&lt;img src=""&gt;&lt;span&gt;Bla bla bla&lt;/span&gt;&lt;/div&gt;
  &lt;/body&gt;
  </code></pre>
  
  <p>Now, in that example, the overhead isn't that great - I could just feed the whole source code, as a string, directly to the regex that finds prices. But normally, pages will have lots of non-text elements that would make the script very slow if I didn't use a fast XPath to parse out the texts. So, I'm looking for an XPath expression that would find the different texts in the example above, <em>but not <strong>just</strong> the text content</em> - since we also need tags that might surround parts of a price (a new &lt;span&gt; will later be created around the matched price, including any inline elements that might surround parts of the price).  </p>
  
  <p>I don't know exactly what the XPath could be made to return, but <em>somehow</em> I need to grab a hold of the following strings from the example page above:</p>
  
  <pre><code>"9.95 &lt;span&gt;EUR&lt;/span&gt;"       (or possibly: "&lt;span&gt;9.95 &lt;span&gt;EUR&lt;/span&gt;&lt;/span&gt;")
  "&lt;span&gt;8.&lt;sup&gt;95&lt;/sup&gt;&lt;/span&gt;AU$"
  "Bla"                         (or possibly: "&lt;th&gt;Bla&lt;/th&gt;")
  "&lt;b&gt;7&lt;/b&gt;.95kr"               (or possibly: "&lt;td&gt;&lt;b&gt;7&lt;/b&gt;.95kr&lt;/td&gt;")
  "Bla bla"                     (or possibly: "&lt;div&gt;Bla bla&lt;/div&gt;")
  "6.95 &lt;span&gt;GBP&lt;/span&gt;"
  "Bla bla bla"                 (or possibly: "&lt;span&gt;Bla bla bla&lt;/span&gt;")
  </code></pre>
  
  <p>and then these strings can be parsed by the regex that finds prices.  </p>