jQuery replace text leaving siblings intact

2019-02-03 15:17发布

问题:

I'm having trouble wrapping my head around what should be a simple solution. I want to replace text within a label tag, without affecting the other 'siblings', if they exist.

Sample markup:

<fieldset class="myFieldsetClass">
    <legend>Sample Fieldset</legend>
    <ol>
        <li>
            <label>
                <span class="marker">*</span>
                Some Label 1
            </label>
        </li>
        <li>
            <label>
                Some Label 2
            </label>
        </li>
        <li>
            <label>
                Text that doesn't match...
            </label>
        </li>
    </ol>
</fieldset> 

Goal:

  • To replace text Some Label X with Some Label (I.e. remove X from the label text).
  • span tags within label must stay intact if they exist, such as the <span class="marker"> above.
  • I do not know the value of X, which could be 1 or more characters long.

Current Script:

My jQuery script below works, but I know is very inefficient. I can't seem to wrap my head around this one for some reason...

//for each label in the fieldset that contains text "Some Label "
$(".myFieldsetClass label:contains('Some Label ')").each(function() {

    if ($(this).has("span").length > 0) {

        //if the label has the span tag within, remove it and prepend it back to the replaced text
        $(this).find("span").remove().prependTo($(this).text(labelText));

    }
    else {

        //otherwise just replace the text
        $(this).text('Some Label');

    }

});  

I thought at first I could simply do:

$(".myFieldsetClass label:contains('Some Label ')").text("Some Label");

But this clears all contents of the label and hence removes the span, which I don't want. I can't use any replace functions to replace Some Label X with Some Label because I don't know what X will be.

Can anyone suggest a more elegant/efficient approach to this problem?

Thanks.

EDIT

After trying multiple answers, I think the problem seems to be that even if I select the right collection, they are text nodes, which jquery doesn't seem to want to modify.. I've used FireBug to select the collection (many answers below all select correctly but in slightly different ways). In firebug console resulting set is:

[<TextNode textContent="Some Label 1:">,
 <TextNode textContent="Some Label 2:">, 
 <TextNode textContent="Some Label 3:">, 
 <TextNode textContent="Some Label 4:">, 
 <TextNode textContent="Some Label 5:">]

The problem seems to be that calling .replaceWith(), .replace(), .text(), etc. doesn't seem to affect the jquery collection. If I allow the above collection to contain one of the spans, then calling .replaceWith(), .replace(), etc functions correctly against the span, but the text nodes stay as is..

回答1:

Try:

$(".myFieldsetClass label:contains('Some Label ')").contents().filter(':last-child').text("Some Label");

This should work assuming the text to be replaced will always be at the end. The contents() function selects all nodes, including text nodes.

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

EDIT: I should have used filter() instead of find(). Corrected.

EDIT: Works now. Here's one way.

// Store proper labels in a variable for quick and accurate reference
var $labelCollection = $(".myFieldsetClass label:contains('Some Label ')");

// Call contents(), grab the last one and remove() it
$labelCollection.each(function() {
    $(this).contents().last().remove()
});

// Then simply append() to the label whatever text you wanted.
$labelCollection.append('some text')


回答2:

As patrick points out, you can use contents() to select the text alone, and then do a replace on it all. Adapting the example given there, you could also try:

$(".myFieldsetClass label:contains('Some Label ')").contents().filter(function() {
  return this.nodeType == 3 && $(this).is(":contains('Some Label ')");
})
.replaceWith("Some Label");

However, if you know that "Some Label " will always be the last element in the <label> then patrick's method will be faster I believe.



回答3:

Why not simply do an entire replace using regex?

$(".myFieldsetClass label:contains('Some Label ')")
    .each(function() {
            $(this).html($(this).html().replace(/Some Label ./, "Some Label")); 
        });


回答4:

A one-liner:

$('.myFieldsetClass label').contents().last().remove().end().first().after('New Label')