views:

527

answers:

2

I'm having a hard time figuring out how to run a method on certain elements/nodetypes but not others.

For instance here's some HTML:

<div id="parent">
    <div class="subparent">Changing Text
        <div class="no-change">Changing Text</div>
    </div>
    <div class="subparent">Changing Text</div>
        <div class="child">Changing Text
            <div class="no-change">Changing Text</div>
        </div>
    <div class="subparent">Changing Text</div>
</div>

My method is something like this:

jQuery.fn.changingtext = function(expr) {
    return this.each(function() {
     this.innerHTML = this.innerHTML
     .replace(/Changing Text/ig, "Edited!")
    });
};

Now I want to change the text of everything except what's in div.no-change. The end result should be something like this:

<div id="parent">
    <div class="subparent">Edited!
        <div class="no-change">Changing Text</div>
    </div>
    <div class="subparent">Edited!</div>
        <div class="child">Edited!
            <div class="no-change">Changing Text</div>
        </div>
    <div class="subparent">Edited!</div>
</div>

I don't know of any way to select a parent without also running the method on its child. Any help would be appreciated. Thanks!

Edit: Here is using Paulo's code and not working: http://jsbin.com/usaxa

Edit@Jeff Meatball Yang: Hi, using your inplace replacement it outputs as text rather than html: http://jsbin.com/idina

I also wasn't able to get the other methods working: http://jsbin.com/aguca

Could you provide an example please?

Thanks!

+7  A: 

I'm not really sure why you're writing a plugin to do this. This would find all the <div> elements inside #parent, filter out those that don't have the class of .no-change, and edit the text contents of them:

$('#parent').find('div').not('.no-change').text('Edited!');

Which could also be written as:

$('#parent div:not(.no-change)').text('Edited!');

jQuery is very good at working on set of elements, you don't have to loop through them and such.

EDIT:

This should work to take account CMS's good observation:

$('#parent').find('div').not('.no-change').each(function() {
    $(this).contents().not('*').eq(0).replaceWith('Whatever');    
});
Paolo Bergantino
+1 But a problem still, the divs are nested, for example, the second div (class 'subparent') he is trying to change only the text node ('Changing Text') and that div has a 'no-change' div nested that will be replaced by the 'text' function, actually both 'no-change' divs are nested inside of divs that will change.
CMS
Hi, I don't believe your code works:http://jsbin.com/usaxaThe reason why I'm using a plugin is because the replacement is actually dozens of replacements and I need to run it in many different places.Thanks again though!
Mark
The code does work and is tested, your translation of it doesn't work. You seem to already have found a solution that helps you, though, so I won't bother posting how to do it the way you want it...
Paolo Bergantino
Hi, please do! I'm sure your solution will be both helpful and educational.
Mark
A year later I come back browsing and I understand why you put this answer. You're selecting specific divs and replacing them with 'Whatever', whereas I actually need to use regex on the innerHTML because my real replacements are more complicated.
Mark
A: 

Try this:

jQuery.fn.changingtext = function(expr) {
    return this.each(function() {
      // filter for text node
      var nodes = $(this).contents().filter(function() { return this.nodeType == 3); });

      // or do in-place editing
      for(var i = 0, len = nodes.length; i < len; i++) {
        nodes[i].nodeValue = nodes[i].nodeValue.replace(/Changing Text/ig, "Edited!");
      }
    });
};

// prove that no change is still white
$("#parent div.no-change").css("color", "white");

// change text, then make it red using jQuery
$("#parent div:not(.no-change)").changingtext().css("color", "red");

The end result is already HTML. So, you will be able to chain other jQuery functions after the plugin call. See my edit.


instead of setting the text node value (nodes[i].nodeValue), you can remove it and append a DIV after it. Instead of using a for loop and replacing the text, you can remove the for loop and use jQuery's built-in functions:

jQuery.fn.changingtext = function(expr) {
    return this.each(function() {
      // filter for text node
      var nodes = $(this).contents().filter(function() { return this.nodeType == 3); });

      // you can replace the text nodes:
      // insert a DIV after each one, 
      // then remove it from the DOM
      nodes.after("<div>Edited!</div>").remove();
    });
};

This is documented in the jQuery Docs (http://docs.jquery.com) on Manipulation and Traversing.

Jeff Meatball Yang
Thanks a lot! This is great! It comes out as a text node, how can I make the end result HTML?Thanks!
Mark
The end result is already HTML. So, you will be able to chain other jQuery functions after the plugin call.
Jeff Meatball Yang
Hi thanks, sorry I don't mean the parent element. I mean the replaced node type. For instance what if my replacement was /Changing (Text)/g, "<div id="$1">Edited</div>". How could I make that into HTML?Thanks!
Mark
Hi, I don't understand your instructions for replacing the textnode so that it will become HTML. Please see my edit.
Mark
Mark, I've tried to clarify - see the second piece of code with my latest edit.
Jeff Meatball Yang