3

I'm trying to remove any occurense of e\E in the div:

HTML:

<div id="container" class="example">
  Some Example text
  <span class="abe">
    <span class="abe">
      this is an inner text of the span
    </span>
    text in the span
  </span>
</div>

CSS

span{color:blue;}​

Javascript (jQuery):

$('div').each(function() {
    $this = $(this);
    $this.text($this.text().replace(/e|E/g, '')); // removes each e\E in the text
});​

LIVE DEMO

For some reason my span get stripped out and only it's inner text remains.
WHY? and how can I fix it?


Update:

I know text gives only the text, I used it becuase I don't want to change the tags attributes. When I used .html it changed <span id="abe"> to <span id="ab">

DEMO

gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 1
    because the call to inner $this.text() returns all the text without html elements, then assigning it back to $this would yield no elements http://jsfiddle.net/Yue67/ – Andreas Wong Mar 28 '12 at 08:27
  • using `.html()` also resulting same http://jsfiddle.net/jKNWb/3/ – tusar Mar 28 '12 at 08:30
  • @tusar. But it mess with the tag itself, check [this](http://jsfiddle.net/jKNWb/4/) out. I updated my answer with that fiddle. – gdoron Mar 28 '12 at 08:32
  • @tusar I'm using chrome and as far as I see it, the `` is preserved – Andreas Wong Mar 28 '12 at 08:34

4 Answers4

4

As noted by others, text() replaces the contents of the element or elements with plain text. You need to iterate over the text nodes within the element and replace characters within their content using the data or nodeValue property of each text node. Here's how to do that with jQuery (using code adapted from this question). 'e' and 'E' characters are replaced with '[X]' for clarity but it's trivial to change.

Demo: http://jsfiddle.net/TqBLu/

Code:

$("#container").find(":not(iframe)").andSelf().contents().each(function() {
    if (this.nodeType == 3) {
        this.data = this.data.replace(/e/ig, "[X]");
    }
});

Here's a version without jQuery for people like me who don't generally use it:

function replaceInTextNodes(node) {
    if (node.nodeType == 3) {
        node.data = node.data.replace(/e/ig, "[X]");
    } else {
        for (var i = 0, len = node.childNodes.length; i < len; ++i) {
            replaceInTextNodes(node.childNodes[i]);
        }
    }
}

replaceInTextNodes(document.getElementById("container"));
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • I think the `not(iframe)` should be `not(span)`. Also I dont understand `nodeType` would you like to explain a bit ? – tusar Mar 28 '12 at 09:50
  • Ohh, I didn't see your answer. in the meanwhile I wrote [this answer](http://stackoverflow.com/a/9905231/601179) with recursion. looks like I did too much work. – gdoron Mar 28 '12 at 10:16
  • @tusar: The `:not(iframe)` bit is there to avoid `contents()` attempting to traverse iframe documents. See http://bugs.jquery.com/ticket/11275. `not(span)` would prevent traversal of text nodes within spans, which is not what is required. As to `nodeType`, that's a standard property of all DOM nodes, and text nodes have a `nodeType` of 3 (also available as `Node.TEXT_NODE`, but the node constants are not supported in older IE). See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247 – Tim Down Mar 28 '12 at 10:22
  • @gdoron: Our answers use essentially the same approach. It's the best way. – Tim Down Mar 28 '12 at 10:24
  • @gdoron: The fastest would be based on plain DOM methods. I may add that to my answer. – Tim Down Mar 28 '12 at 10:41
  • The [jsperf](http://jsperf.com/ways-to-find-text-nodes). Plain javascript is the winner. – gdoron Mar 29 '12 at 07:12
2

Setting the textContent or innerText (which is what text() does) removes all tags from the element.

Aside from that, you were only getting the text in the first place (minus tags), and putting it back in. You wiped out the span in two different ways.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • Check my update, `.html` gives my the tag, so `` becomes ``. I don't want to touch the tags, only, it's content. any ideas? – gdoron Mar 28 '12 at 08:34
  • 2
    Loop through child nodes. If a node is a tag, recurse. If it's a text node, replace. – Niet the Dark Absol Mar 28 '12 at 08:46
  • You may be interested with the result. read [this](http://stackoverflow.com/questions/9903668/why-is-my-span-stripped-out#comment12661843_9904107) comment. thanks for the answer. – gdoron Mar 29 '12 at 07:17
0

This is a solution with negative lookahead (?! ...): http://jsfiddle.net/Sqkud/1/
In this way I apply a replacement of e character only if it's not contained in angular brackets (as a substring of the tagname or attribute name/value)

$('div').each(function() {
    $this = $(this);
    $this.html($this.html().replace(/(?!.*<.*[e]*.*>.*)e*/gi, ''));
});
Fabrizio Calderan
  • 120,726
  • 26
  • 164
  • 177
  • [The pony he comes](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454) - What if the HTML contains unescaped `<` or `>` that should be escaped? – Niet the Dark Absol Mar 28 '12 at 08:55
  • good point kolink: of course it's a workaround and its usage depends on what kind of markup has to be handled with that regexp. – Fabrizio Calderan Mar 28 '12 at 08:58
  • I really wouldn't recommend a regex solution: it's unreliable, slow and overcomplicated, and a simple, reliable solution exists: iterate over the text nodes. – Tim Down Mar 28 '12 at 09:07
  • @TimDown. Actually your first answer is the slowest... **:)** [mine](http://stackoverflow.com/a/9905231/601179) (with jQuery) is the fastest. [jsperf](http://jsperf.com/ways-to-find-text-nodes). your's update with plain javascript is faster then jQuery(obviously). – gdoron Mar 29 '12 at 07:15
  • @gdoron: Fair enough, that's one reason not to use a regex gone, only leaving about another 99 :) – Tim Down Mar 29 '12 at 09:08
0

I found a way of doing it with recursion:

Javascript:

function change(node) {
    if (node.nodeType == 3) {
        node.data = node.data.replace(/e|E/g, '');
    }
    else {
        $(node).contents().each(function() {
            change(this);
        });
    }
}

$('div').contents().each(function() {
    change(this);
});

Based on this HTML:

<div class="example">Some Example text  
    <span class="abe">
        <span class="abe"> this is an inner text of the span </span>  text in the span 
    </span>
</div>
<div class="example">Some more Example text</div>​        
​

LIVE DEMO

gdoron
  • 147,333
  • 58
  • 291
  • 367