-2
<div class="side">
    <a>Link 1</a>
    <a>Link 2</a>
    <input value="1"/>
      / 12
</div>

I have the above and I'm trying to figure out how to wrap the / 12 with <span> the 12 does change.

I've tried

var input = angular.element( document.querySelectorAll( '.side input'));
angular.element(input).append('<span>');

desired outcome

<div class="side">
    <a>Link 1</a>
    <a>Link 2</a>
    <span class="page-count">
        <input value="1"/>
          / 12
    </span>
</div>
Rhumborl
  • 16,349
  • 4
  • 39
  • 45
ngplayground
  • 20,365
  • 36
  • 94
  • 173
  • Since you are using angularjs why not just make a `` in your html template that holds an angular expression? for example `{{page_count}}` where `page_count` is some `$scope` property that holds your page count. Then you wouldn't have to worry about modifying html – Patrick Evans Feb 03 '16 at 15:56
  • @PatrickEvans It's from a core vendor and rather not change that in the event the vendor gets updated when running bower – ngplayground Feb 03 '16 at 16:00
  • You need this: https://api.jquery.com/closest/ – Joao Polo Feb 03 '16 at 16:36

3 Answers3

1

wrap() alone will not work, if you want to wrap a group of elements (the input and the "/12" text node) , as it wraps each one individually.

You need to find the relevant nodes, i.e. the input and text nodes, detach() them from the DOM, append() them to your new span, then append the span to the original div.

You can include text nodes by using contents() and checking the nodeType as described in this answer.

jQuery

With full jQuery, this isn't too bad:

$('.side').append(function() {

    // create the span, add the detached nodes to it, then append to the container
    $('<span class="page-count"/>').append($(this).contents().filter(function(){

        // filter() contents down to textNodes and the input element, then detach() them all in one jQuery object
        return((this.nodeType === 3 && this.textContent.trim().length > 0)
            || (this.tagName && this.tagName.toUpperCase() === 'INPUT'))

    }).detach()).appendTo(this);

});

$('.side').append(function() {

    // create the span, add the detached nodes to it, then append to the container
 $('<span class="page-count"/>').append($(this).contents().filter(function(){

        // filter() contents down to textNodes and the input element, then detach() them all in one jQuery object
       return((this.nodeType === 3 && this.textContent.trim().length > 0)
         || (this.tagName && this.tagName.toUpperCase() === 'INPUT'))

    }).detach()).appendTo(this);

});
span.page-count {
  border:1px solid red;
  padding:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="side">
    <a>Link 1</a>
    <a>Link 2</a>
    <input value="1"/>
      / 12
</div>

Angular

With jqLite, it's not quite as neat. Just replacing $(this) with angular.element(this) doesn't work, even though most of these jQuery functions are supported in jqLite. The problem comes down to filter() being unsupported, which makes it awkward to detach() all the necessary elements in one go.

I got around this by changing the statement above into a forEach() and detach/appending one at a time:

// store the '.side' container
var sideElem = angular.element(document.querySelectorAll('.side'));
// create the new span
var pageCount = angular.element('<span class="page-count"/>');

// loop through contents of '.side'
angular.forEach(sideElem.contents(), function(val) {
    // if node matches, detach() it from '.side' and append() to the span
    if((val.nodeType === 3 && val.textContent.trim().length > 0)
        || (val.tagName && val.tagName.toUpperCase() === 'INPUT')) {
    pageCount.append(angular.element(val).detach());
  }
});

// add the span to the '.side' container
sideElem.append(pageCount);

// store the '.side' container
var sideElem = angular.element(document.querySelectorAll('.side'));
// create the new span
var pageCount = angular.element('<span class="page-count"/>');
    
// loop through contents of '.side'
angular.forEach(sideElem.contents(), function(val) {
    // if node matches, detach() it from '.side' and append() to the span
    if((val.nodeType === 3 && val.textContent.trim().length > 0)
        || (val.tagName && val.tagName.toUpperCase() === 'INPUT')) {
        pageCount.append(angular.element(val).detach());
    }
});
    
// add the span to the '.side' container
sideElem.append(pageCount);
span.page-count {
  border:1px solid red;
  padding:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div class="side">
    <a>Link 1</a>
    <a>Link 2</a>
    <input value="1"/>
      / 12
</div>
Community
  • 1
  • 1
Rhumborl
  • 16,349
  • 4
  • 39
  • 45
0

I think you can use nextSibling to achieve that.

It would look something like this:

var input = angular.element( document.querySelectorAll( '.side input'));
var textNode = input.nextSibling();
angular.element(textNode).append('<span>');

I am not entirely sure though, but I think something close to it should do the job.

Jonas Meinerz
  • 612
  • 3
  • 9
-1
var lastText = addDiv[0].childNodes;
var lastTextIndex = addDiv[0].childNodes.length;
angular.element( lastText[lastTextIndex-1]).wrap('<span class="page-count">')
ngplayground
  • 20,365
  • 36
  • 94
  • 173