4

I have the following HTML snippet:

<span class="target">Change me <a class="changeme" href="#">now</a></span>

I'd like to change the text node (i.e. "Change me ") inside the span from jQuery, while leaving the nested <a> tag with all attributes etc. intact. My initial huch was to use .text(...) on the span node, but as it turns out this will replace the whole inner part with the passed textual content.

I solved this with first cloning the <a> tag, then setting the new text content of <span> (which will remove the original <a> tag), and finally appending the cloned <a> tag to my <span>. This works, but feels such an overkill for a simple task like this. Btw. I can't guarantee that there will be an initial text node inside the span - it might be empty, just like:

<span class="target"><a class="changeme" href="#">now</a></span>

I did a jsfiddle too. So, what would be the neat way to do this?

András Szepesházi
  • 6,483
  • 5
  • 45
  • 59
  • Possible duplicate of **[In jQuery how can I change an element's text without changing its child elements?](http://stackoverflow.com/questions/4106809)** or **[jQuery - setting an element's text only without removing other element (anchor)](http://stackoverflow.com/questions/6156470)** – hippietrail Oct 11 '12 at 06:25

8 Answers8

6

Try something like:

$('a.changeme').on('click', function() {
  $(this).closest('.target').contents().not(this).eq(0).replaceWith('Do it again ');
});

demo: http://jsfiddle.net/eEMGz/

ref: http://api.jquery.com/contents/

Update:

I guess I read your question wrong, and you're trying to replace the text if it's already there and inject it otherwise. For this, try:

$('a.changeme').on('click', function() {
  var
    $tmp = $(this).closest('.target').contents().not(this).eq(0),
    dia = document.createTextNode('Do it again ');

  $tmp.length > 0 ? $tmp.replaceWith(dia) : $(dia).insertBefore(this);
});

​Demo: http://jsfiddle.net/eEMGz/3/

Yoshi
  • 54,081
  • 14
  • 89
  • 103
  • It seems like this might be a good place to extend jQuery higher up in the code, just to keep the working code clean; do you think so? – Code Junkie Feb 28 '12 at 19:00
  • @CodeJunkie I'm not sure. If this was just over and over again, then a plugin might be ok, but otherwise I'd stick without extending jquery for such little tasks. – Yoshi Feb 28 '12 at 20:00
1

You can use .contents():

//set the new text to replace the old text
var newText = 'New Text';

//bind `click` event handler to the `.changeme` elements
$('.changeme').on('click', function () {

    //iterate over the nodes in this `<span>` element
    $.each($(this).parent().contents(), function () {

        //if the type of this node is undefined then it's a text node and we want to replace it
        if (typeof this.tagName == 'undefined') {

            //to replace the node we can use `.replaceWith()`
            $(this).replaceWith(newText);
        }
    });
});​

Here is a demo: http://jsfiddle.net/jasper/PURHA/1/

Some docs for ya:

Update

var newText = 'New Text';
$('a').on('click', function () {
    $.each($(this).parent().contents(), function () {
        if (typeof this.tagName == 'undefined') {

            //instead of replacing this node with the replacement string, just replace it with a blank string
            $(this).replaceWith('');
        }
    });

    //then add the replacement string to the `<span>` element regardless of it's initial state
    $(this).parent().prepend(newText);
});​

Demo: http://jsfiddle.net/jasper/PURHA/2/

Community
  • 1
  • 1
Jasper
  • 75,717
  • 14
  • 151
  • 146
  • "Btw. I can't guarantee that there will be an initial text node inside the span - it might be empty" this test case fails. – András Szepesházi Feb 28 '12 at 18:23
  • @AndrásSzepesházi I didn't realize you wanted to add the new string to empty containers as well. I updated my answer, here's a demo: http://jsfiddle.net/jasper/PURHA/2/ – Jasper Feb 28 '12 at 18:25
1

You can try this.

var $textNode, $parent;
$('.changeme').on('click', function(){
    $parent = $(this).parent(); 
    $textNode= $parent.contents().filter(function() {
            return this.nodeType == 3;
    });
    if($textNode.length){
        $textNode.replaceWith('Content changed')
    }
    else{
        $parent.prepend('New content');
    }
});

Working demo - http://jsfiddle.net/ShankarSangoli/yx5Ju/8/

ShankarSangoli
  • 69,612
  • 13
  • 93
  • 124
1

You step out of jQuery because it doesn't help you to deal with text nodes. The following will remove the first child of every <span> element with class "target" if and only if it exists and is a text node.

Demo: http://jsfiddle.net/yx5Ju/11/

Code:

$('span.target').each(function() {
    var firstChild = this.firstChild;
    if (firstChild && firstChild.nodeType == 3) {
        firstChild.data = "Do it again";
    }
});
Tim Down
  • 318,141
  • 75
  • 454
  • 536
0

This is not a perfect example I guess, but you could use contents function.

console.log($("span.target").contents()[0].data);
DG3
  • 5,070
  • 17
  • 49
  • 61
0

You could wrap the text into a span ... but ...

try this. http://jsfiddle.net/Y8tMk/

$(function(){
    var txt = '';
    $('.target').contents().each(function(){
        if(this.nodeType==3){
                    this.textContent = 'done ';
        }
    });
});
Mikhas
  • 851
  • 1
  • 12
  • 31
0

You can change the native (non-jquery) data property of the object. Updated jsfiddle here: http://jsfiddle.net/elgreg/yx5Ju/2/

Something like:

$('a.changeme3').click(function(){
    $('span.target3').contents().get(0).data = 'Do it again';
});

The contents() gets the innards and the get(0) gets us back to the original element and the .data is now a reference to the native js textnode. (I haven't tested this cross browser.)

This jsfiddle and answer are really just an expanded explanation of the answer to this question: Change text-nodes text

Community
  • 1
  • 1
elgreg
  • 125
  • 1
  • 8
0
$('a.changeme').click(function() {                         
    var firstNode= $(this).parent().contents()[0];
        if( firstNode.nodeType==3){
        firstNode.nodeValue='New text';
    }
})

EDIT: not sure what layout rules you need, update to test only first node, otherwise adapt as needed

charlietfl
  • 170,828
  • 13
  • 121
  • 150