views:

391

answers:

3

Hi,

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..

+4  A: 

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')
patrick dw
Thanks for the suggestion. I found one problem, in that `.contents().filter(':last')` returns only the last item in the entire collection, where the contents collection contains many label text nodes. Additionally, .text() applied to these nodes doesn't seem to take affect...
KP
But if you're calling it on a label, `contents()` should only return the contents of that label. As far as `text()` is concerned, you should be able to simply `remove()` that text node, and `append()` new text.
patrick dw
I see. I'll bet 'last-child' is needed.
patrick dw
Finally actually tested it. Works now. :op
patrick dw
Thanks Patrick this solution works well. I was hoping to a one liner but I don't think it's possible. Your answer is more elegant than the one I original did so I'll be using yours. Thanks again to all for the help!
KP
+2  A: 

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.

Sean Vieira
Ok strangest thing. It finds the collection of nodes, but the `.replaceWith("Some Label");` call actually clears all of the text node values (labels texts become empty, spans stay in tact)...
KP
@KP; I've updated it -- using `is()` in the filter condition allows you to have multiple text nodes and only change the one that contains "Some Label" ... let me know if this doesn't fix the problem
Sean Vieira
@KP, used this in IE and it worked fine (pre the .is filter change). Alternatively, you can always go back to POJ and call ...nodeType == 3).each(function() { $(this)[0].data = "Some Label"; });
Marc
Thanks Sean. I chose Patrick's solution, but upvoted yours as it's a valid way to go as well..
KP
+1  A: 

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")); 
        });
David Morton
Not a bad idea. The replace isn't working though? `.replace("/Some Label ./", "Some Label"))` isn't matching the text. I ensured the overall selector `$(".myFieldsetClass label:contains('Some Label ')")` is grabbing the desired elements..
KP
+1 for good idea to use regex. Pattern didn't match and regex is not my strength so I chose a different answer..
KP