views:

1810

answers:

4

Hello

I have this:

  <ul id="pickme">
    <li>1</li>
    <li>2</li>
    <li>3
      <ul>
        <li>3-1</li>
      </ul>
    </li>
  </ul>

But want this:

  <ul id="pickme">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li class="new_ul">
      <ul>
        <li>3-1</li>
      </ul>
    </li>
  </ul>

With other words I need to insert

</li><li class="new_ul">

after the 3 in the list. I have played with .append and .prepend but witout beeing able to insert at the right place.

Any help would be appreciated

Br. Anders


There is a correct answer below. If you use that then it will run through only one level. But same example is is capable of running through multible levels. Just change ('>li>ul') to ('ul') and all nested ul's will be found and handled. Perfect!

(will run through endless levels of nested ul's)

$('#pickme').find('ul').each(function() {
    $(this).wrap('<li class="new_ul"></li>').parent().insertAfter($(this).parent().parent());
});

In this case this:

  <ul id="pickme">
    <li>1</li>
    <li>2</li>
    <li>3
      <ul>
        <li>3-1
          <ul>
            <li>3-1-1</li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>

Will be:

  <ul id="pickme">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li class="new_ul">  <<new li
      <ul>
        <li>3-1</li>
        <li class="new_ul">  <<new li
          <ul>
            <li>3-1-1</li>
          </ul>
        </li>
      </ul>
    </li>
    </li>
  </ul>
A: 

Use .html(), which will get you the inner text. Manipulate the string. Then use .html(newHtml) to set the new inner text.

Your problem with prepend and append is that you don't need to prepend or append, you need to insert in the middle (after the 3).

Russell Steen
Manipulating HTML as a string is tedious and unreliable. Setting `html()` re-parses all nodes, losing any non-serialisable properties. Best avoided.
bobince
So provide a better answer.
Russell Steen
Thanks. I have done a bit of reading on .html. I ended up with this (not quite right yet): var url_html = $(".sitemap-0 ul").parent().html(); $(".sitemap-0 ul").parent().html("</li><li class='nextLevel'>" + url_html);but for some reason the first </li> is not inserted. Is html validated before insert so an end-tag cannot be inserted before a start-tag? I am confused
Tillebeck
+1  A: 

This will work for you;

// get the #pickme element
var pickme = $('#pickme');

// find all the LI elements that are directly under #pickme
var pickmeLIs = pickme.find('>li');

// find all the UL elements that are directly under our found LI elements
var pickmeULs = pickmeLIs.find('>ul');

// loop through our UL elements
pickmeULs.each(function() {

    // wrap this UL element in the new LI element
    $(this).wrap('<li class="new_ul"></li>');

    // select the wrapping LI we just added to the UL element
    var wrapingLI = $(this).parent();

    // find our original parent LI element
    var originalParent = $(this).parent().parent();

    // move this and the new wrapping LI to the right place
    wrapingLI.insertAfter(originalParent);
});

this can be condensed down to;

$('#pickme').find('>li >ul').each(function() {
    $(this).wrap('<li class="new_ul"></li>').parent().insertAfter($(this).parent().parent());
});
Matt Smith
Looks like a good approach, but is two `parent()` at the end right? We'd want to insertAfter the parent `li`, surely?
bobince
Yes, but by the time we get around to passing the target into the insertAfter we have already wrapped it in an LI element with the wrap function.
Matt Smith
Oh, good point! Yep, nice work.
bobince
You could move it then wrap it, that would allow you to remove the second parent() call.
Matt Smith
Thaks. This is to complext for me. I will have to start a fresh one tomorrow and look into your code and try to understand it.Br. Anders
Tillebeck
I'll write you some comments to explain in a long winded version
Matt Smith
What can I say... it works! I have used your commented version and try to understand it by going through the steps. It is great. I will post my findings back here. Thanks a lot. Very impressive.
Tillebeck
I think of a block of html as a string. What I learned is that jQuery reads the same 'string' as a collection of elements. Otherwise I cannot undestand how it would be possible to wrap and ul that is already within an li without getting errors in the 'html-string'. For me there are still elemenst of magick.
Tillebeck
you've got it right there. basically when you put a string of html into the jQuery object it converts that into a document fragment `document.createDocumentFragment()` which is basically html but not attached to the document body yet. you can do all the same things as in something attached to the document.
Matt Smith
A: 

last-child selector will ensure that you add new item after it.

You can use that as

$('<li class="new_ul">4</li>').appendTo("ul#pick_me > li:last-child");

Myra
Thanks. I cannot get it to work. But apart from that the appendTo is great! I am guite new with jQuery so having a lot af Ahhhhh experiences these days
Tillebeck
+1  A: 

If you have a way of knowing how many list items there are in #pickme you can use $('#pickeme li:eq(theIndex)') otherwise, use this:

$('#pickme li:last').after('<li class="new_ul"><ul><li>3-1</li></ul></li>');

Tested and works.

UPDATE

Thanks to bobince and Myra I now have the following which does exactly what you're asking for:

$('#pickme li:last-child').replaceWith('<li class="new_ul"><ul><li>3-1</li></ul></li>');

Output:

<ul id="pickme">
    <li>1</li>
    <li>2</li>
    <li class="new_ul">
      <ul>
        <li>3-1</li>
      </ul>
    </li>
</ul>
jeerose
But leaves the original 3-1 in place.
bobince
Modified... thanks bobince.
jeerose
You aren't moving the elements around there you are changing the html of the page. what if there are already some JavaScript events on the list items you've removed.
Matt Smith
My impression was that he wanted to insert something new rather than move things around but you're absolutely right Matt.
jeerose
Wow! What a lot of replies! I am new to this site, the help is overwhelming. Thanks.To clarify: I know nothing of the content of any li. The only static part is this string "</li><li class='new_ul'>" that should be inserted just before any nested ul. Since I do not know jQuery very well I (wrongly) expected this to be a trivial task good for a beginner to practice on :-) I see that is not the case. But I am still learning a lot so I are apreciationg all answers and comments.
Tillebeck
Jeeros >> your solution will move the last li to a nested ul, correct? That is cool :-) But in my case I need: 1) find an li with a ul inside. 2) add another li and move the ul to this new li.
Tillebeck
I would guess the easiest way would be to find any nested ul's ('#pickme ul') and insert this html right before that ul "</li><li class='ned_ul'>"
Tillebeck
Tillebeck, some more description in your question would go a long way to a short answer next time! Welcome to Stack Overflow and remember to be as clear as possible in your questions. Cheers.
jeerose