skip to Main Content

I have two unordered lists inside a div with a known class. I need to change the list items in the first list to spans with the class "time". Next I would like to change the second list items to spans with the class "frequency". Finally I would like to remove the <ul> tags leaving only the spans behind in the div. Just like:

<div id="training-labels">
<ul>
<li>30 mins</li>
</ul>

<ul>
<li>Annually</li>
<li>First 2 weeks</li>
</ul>
</div>

to this

<span class="time">30 mins</span>
<span class="frequency">Annually</span>
<span class="frequency">First 2 weeks</span>

I have managed to change the contents of each list into spans:

$('#training-labels ul:nth-of-type(1) li').wrapInner('<span class="time" />').contents();
$('#training-labels ul:nth-of-type(2) li').wrapInner('<span class="frequency" />').contents();

But have not been successful stripping out the ul and li tags, leaving only the spans behind.

Is this the best approach or is there a better way to achieve this with JQuery?

I have set up a Fiddle here

$('#training-labels ul:nth-of-type(1) li').wrapInner('<span class="time" />').contents();

$('#training-labels ul:nth-of-type(2) li').wrapInner('<span class="frequency" />').contents();
.time {
  color: green;
}

.frequency {
  color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="training-labels">
  <ul>
    <li>30 mins</li>
  </ul>

  <ul>
    <li>Annually</li>
    <li>First 2 weeks</li>
  </ul>
</div>

4

Answers


  1. You can do following:

    $('#training-labels ul:nth-of-type(1) li').wrap('<span class="time"/>').contents().unwrap();
    
    $('#training-labels ul:nth-of-type(2) li').wrap('<span class="frequency"/>').contents().unwrap();
    
    $('#training-labels ul:nth-of-type(1) span').unwrap();
    $('#training-labels ul:nth-of-type(1) span').unwrap();
    
      
    .time {
      color: green;
    }
    
    .frequency {
      color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="training-labels">
      <ul>
        <li>30 mins</li>
      </ul>
    
      <ul>
        <li>Annually</li>
        <li>First 2 weeks</li>
      </ul>
    </div>

    JQuery wrap() method:

    The .wrap() function can take any string or object that could be passed to the $() factory function to specify a DOM structure.

    JQuery unwrap() method:

    Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place.

    Note: Since no class is attached to the parent div (with the id training-labels), both the spans are in the same row.

    Login or Signup to reply.
  2. If we can be sure all ul1 has times and all ul2 has frequencies

    const $div = $('#training-labels');
    const $uls = $('ul', $div); // save
    $div.empty(); // clear
    $uls.each(function(i, ul) {
      $("li", ul).each(function(j, li) {
        $(`<span class="${i===0 ? "time" : "frequency" }">${li.textContent}</span><br/>`)
          .appendTo($div)
      });
    });
    .time {
      color: green;
    }
    
    .frequency {
      color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="training-labels">
      <ul>
        <li>30 mins</li>
      </ul>
      <ul>
        <li>Annually</li>
        <li>First 2 weeks</li>
      </ul>
    </div>

    Otherwise

    const $div = $('#training-labels')
    $('ul:nth-of-type(1) li', $div).wrapInner('<span class="time" />').contents();
    $('ul:nth-of-type(2) li', $div).wrapInner('<span class="frequency" />').contents();
    $div.html(function() { 
      return $div.find("span")
        .map(function() { return this.outerHTML })
        .get()
        .join("<br/>"); // or space or something else
    });
    .time {
      color: green;
    }
    
    .frequency {
      color: red;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="training-labels">
      <ul>
        <li>30 mins</li>
      </ul>
      <ul>
        <li>Annually</li>
        <li>First 2 weeks</li>
      </ul>
    </div>
    Login or Signup to reply.
  3. My approach

    let $div = $('#training-labels');
    let $time = $('ul:nth-of-type(1) li', $div).clone().wrapInner('<span class="time" />').contents();
    let $freq = $('ul:nth-of-type(2) li', $div).clone().wrapInner('<span class="frequency" />').contents();
    $div.replaceWith($().add($time).add($freq));
    

    This is not dissimilar from the unwrap solution except that it is not so tightly tied to the tag structure. The clone part isn’t strictly required but it means that there is actually only one update to the DOM which happens after the entire replacement has been generated.

    Login or Signup to reply.
  4. While you’ve already accepted an answer, I wanted to take the time to show a non-JavaScript (or jQuery) CSS-only approach that may work for you, depending on the desired end-result:

    /* a simple reset to remove default font-sizes, box-sizing, margins and padding: */
    *, ::before, ::after {
      box-sizing: border-box;
      font-family: system-ui;
      font-size: 16px;
      margin: 0;
      padding: 0;
    }
    
    /* removing default list-markers from the <li> elements: */
    ul {
      list-style-type: none;
    }
    
    /* wrapping the outer <div> elements which wrap your <ul>
       elements in a <main> element in order to style and
       align the content; this is unnecessary for the demo
       functionality: */
    main {
      /* taking advantage of grid display to position items on
         the vertical (block) axis: */
      display: grid;
      /* separating tracks with a 'gutter': */
      gap: 1em;
      /* defining the inline-size - the size of the element
         along the inline-axis (horizontal in most latin-
         derived languages, such as French, English...): */
      inline-size: clamp(15rem, 70vw, 1100px);
      /* a 1em margin before the start of the element on its
         block axis (perpendicular to the inline axis,
         vertical in most Latin-derived languages): */
      margin-block-start: 1em;
      /* using margins to centre the element: */
      margin-inline: auto;
    }
    
    /* default styles for the .content elements (the class-name
       given to the <div> elements which wrap the <ul> elements:*/
    .content {
      border: 2px solid var(--color, currentColor);
      padding: 0.5em;
    }
    
    /* styling the <li> elements as inline-block; which allows them
       to flow inline but take a specified size (inline or block),
       and margins: */
    #inlineBlock li {
      display: inline-block;
    }
    
    /* using flex layout on the <ul> elements, which positions the
       <li> children along the inline-axis (by default) while allowing
       them to be sized according to their content or declaration: */
    #flex ul {
      display: flex;
    }
    
    /* this was posted purely for the sake of completeness, and I don't
       recommend it since the track sizing looks more tabular than
       is possibly required: */
    #grid {
      display: grid;
      /* this defines two columns with each column sized to fit the
         content within: */
      grid-template-columns: repeat(2, fit-content);
    }
    
    #grid ul {
      /* we use this to effectively pretend the <ul> elements don't
         exist, to allow the <li> elements to be placed into the
         grid rather than the <ul> elements: */
      display: contents;
    }
    
    /* because each row/column track of a grid has an equal number of
       divisions/"cells", and because we're pretending (display: contents)
       that the <ul> elements don't exist, this is to ensure that
       the <li> contents of the second <ul> don't fill the first row track;
       this can be adjusted if you'd prefer that to happen, but I didn't
       expect that it's required: */
    #grid li:only-child {
      grid-column: span 2;
    }
    
    /* going back to the land before time, we use a simple floats to
       allow the <li> elements to run inline: */
    #float li {
      float: left;
    }
    
    /* to ensure the first <li> always starts on a new line: */
    #float li:first-child {
      clear: left;
    }
    
    /* selecting all <li> elements that are not the :first-child,
       contained within a <div> that does not have an id of "grid": */
    .content:is(:not(#grid)) li:not(:first-child)::before {
      /* placing a comma before those elelements: */
      content: ", ";
    }
    
    /* selecting the :last-child <li> descendant of .content elements
       (which do not have the id of "grid"), and styling their ::after
       pseudo-element: */
    .content:is(:not(#grid)) li:last-child::after {
      /* adding a full-stop/period: */
      content: '.';
    }
    
    /* styling the ::after pseudo-element of an <li> which is the :only-child
       of its parent, and within a .content element without the id of "grid": */
    .content:is(:not(#grid)) li:only-child::after {
      /* adding a colon after the text of the element: */
      content: ': ';
    }
    <main>
      <div id="inlineBlock" class="content" style="--color: hsl(120deg 80% 70% / 1)">
        <ul>
          <li>30 mins</li>
        </ul>
    
        <ul>
          <li>Annually</li>
          <li>First 2 weeks</li>
        </ul>
      </div>
      
      <div id="flex" class="content" style="--color: hsl(160deg 80% 70% / 1)">
        <ul>
          <li>30 mins</li>
        </ul>
    
        <ul>
          <li>Annually</li>
          <li>First 2 weeks</li>
        </ul>
      </div>
      
      <div id="grid" class="content" style="--color: hsl(200deg 80% 70% / 1)">
        <ul>
          <li>30 mins</li>
        </ul>
    
        <ul>
          <li>Annually</li>
          <li>First 2 weeks</li>
        </ul>
      </div>
      
      <div id="float" class="content" style="--color: hsl(240deg 80% 70% / 1)">
        <ul>
          <li>30 mins</li>
        </ul>
    
        <ul>
          <li>Annually</li>
          <li>First 2 weeks</li>
        </ul>
      </div>
    </main>

    JS Fiddle demo.

    References:

    CSS:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search