2

I'm trying to find a way to wrap just the inner text of an element, I don't want to target any other inner dom elements. For example.

<ul>
  <li class="this-one"> 
  this is my item
   <ul>
    <li>
       this is a sub element
    </li>
   </ul>
  </li>
</ul>

I want to use jQuery to do this.

<ul>
   <li class="this-one"> 
       <div class="tree-item-text">this is my item</div>
       <ul>
        <li>
           <div class="tree-item-text">this is a sub element</div>
        </li>
       </ul>
      </li>
    </ul>

A little background is I need to make an in-house tree structure ui element, So I'm using the UL structure to represent this. But I don't want developers to have to do any special formatting to use the widget.

update: I just wanted to add the purpose of this is I want to add a click listener to be able to expand the elements under the li, However, since those elements are within the li the click listener will activate even when clicking on the children, So I want to attach it to the text instead, to do this the text needs to be targetable, which is why I want to wrap it in a div of it's own.

So far I've come up with wrapping all the inner elements of the li in a div and then moving all inner dom elements back to the original parent. But this code is pretty heavy for something that might be much simpler and not require so much DOM manipulation.

EDIT: Want to share the first pseudo alternative I came up with but I think it is very tasking for what I want to accomplish.

var innerTextThing = $("ul.tree ul").parents("li").wrapInner("<div class='tree-node-text'>");

$(innerTextThing.find(".tree-node-text")).each(function(){
   $(this).after($(this).children("ul"));
});

Answered: I ended up doing the following, FYI i only have to worry about FF and IE compatibility so it's untested in other browsers.

    //this will wrap all li textNodes in a div so we can target them.
    $(that).find("li").contents()
      .filter(function () {
          return this.nodeType == 3;
      }).each(function () {
          if (
          //these are for IE and FF compatibility
                (this.textContent != undefined && this.textContent.trim() != "")
                ||
                (this.innerText != undefined && this.innerText.trim() != "")
              ) {
              $(this).wrap("<div class='tree-node-text'>");
          }
      });
Chris Stephens
  • 2,164
  • 2
  • 17
  • 21

3 Answers3

5

Use wrapInner():

​$('li').wrapInner('<div class="tree-item-text" />');​

JS Fiddle demo.


Edited to address the concern, raised by the OP, in comments below:

wrap inner will wrap all the elements inside the li with the div, i just want to target the text.

This amended code should wrap only textNodes:

$('li').each(
    function(){
        var kids = this.childNodes;
        for (var i=0,len=kids.length;i<len;i++){
            if (kids[i].nodeName == '#text'){
                $(kids[i]).wrap('<div class="tree-item-text" />');
            }
        }
    });​

JS Fiddle demo.

References:

David Thomas
  • 249,100
  • 51
  • 377
  • 410
3

In case you'd like some more options, here's a question with several other ways of finding text nodes: How do I select text nodes with jQuery?

Community
  • 1
  • 1
Zugbo
  • 514
  • 1
  • 4
  • 14
  • 1
    I ended up going with a modified version of one of the answers. `$(elem).contents().filter(function() {return this.nodeType ==3;});` – Chris Stephens Mar 22 '12 at 23:16
1

What about using some regular expressions? Assume your root UL has ID myTree then:

// Get all the HTML inside the UL.
var html = $("#myTree").html();  
// Wrap all text nodes with DIVs.
html = html.replace(/>([^<]+)</gi, "><div class='tree-item.text'>$1</div>");  
// Put it back.
$("#myTree").html(html); 
Diego
  • 18,035
  • 5
  • 62
  • 66
  • well I believe this will make any elements within the li loose any bound listeners. I need to avoid that so developers don't have to worry about when they bind to elements. – Chris Stephens Mar 16 '12 at 22:52