3

Is it possible to do the following with jQuery so that this:

<h2>
    This is a test 
    <span>i</span>
    mag
    <span>i</span>
    nat
    <span>i</span>
    on  
</h2>

Becomes this:

<h2>
    This is a test 
    <span class="wrap">
       <span>i</span>
       mag
       <span>i</span>
       nat
       <span>i</span>
       on   
    </span>
</h2>

So I want the outer span to wrap all the inner spans.

Don't feel this is a duplicate question as I want to just wrap the all the spans and not the text "This is a test".

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
Rob
  • 6,304
  • 24
  • 83
  • 189

2 Answers2

4

You need to use $.wrapAll():

$(function () {
  $("h2 span").wrapAll('<span class="wrap" />');
});
.wrap {background: #ccf;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<h2>
  This is a test 
  <span>Test</span>
  <span>Test</span>
  <span>Test</span>   
</h2>
Praveen Kumar Purushothaman
  • 164,888
  • 24
  • 203
  • 252
  • That's wrapped the spans but the word inside each of those spans are now sitting outside of the `wrap` span. – Rob Jan 13 '16 at 15:45
  • Sorry my fault, I should've used the real example. I've updated my question. Essentially the inner spans wrap the i's in the word imagination. I want the entire word (along with the inner spans) to be wrapped with an outer span. Can that be done? – Rob Jan 13 '16 at 15:51
  • @Rob Er... Okay... Will see if that's possible. – Praveen Kumar Purushothaman Jan 13 '16 at 15:51
2

Based on your update, you could filter the contents of the h2 element depending on whether this is a span element or if this.previousSibling is a span element:

$('h2').contents().filter(function () {
  return $(this).is('span') || $(this.previousSibling).is('span');
}).wrapAll('<span class="wrap" />');
.wrap { color: #f00; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
    This is a test 
    <span>i</span>
    mag
    <span>i</span>
    nat
    <span>i</span>
    on  
</h2>

Alternatively, you could also filter the elements and text nodes and exclude the text nodes with trimmed text that equals 'This is a test':

$('h2').contents().filter(function () {
  return this.textContent.trim() !== 'This is a test';
}).wrapAll('<span class="wrap" />');
.wrap { color: #f00; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
    This is a test 
    <span>i</span>
    mag
    <span>i</span>
    nat
    <span>i</span>
    on  
</h2>

You could also just exclude the first text node as well:

$('h2').contents().filter(function () {
  return this !== this.parentElement.firstChild;
}).wrapAll('<span class="wrap" />');
.wrap { color: #f00; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>
    This is a test 
    <span>i</span>
    mag
    <span>i</span>
    nat
    <span>i</span>
    on  
</h2>
Josh Crozier
  • 233,099
  • 56
  • 391
  • 304