Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do to wrap a span around a section of text without using jQuery

Tags:

javascript

<p>Lorem Ipsum <a href="#">Link</a> <div ... </div> </p>

I want to put a span around 'Lorem Ipsum' without using jQuery, so the result looks like:

<p><span>Lorem Ipsum </span><a href="#">Link</a> <div ... </div> </p>

Any ideas? Thanks

like image 500
patad Avatar asked Dec 02 '09 21:12

patad


People also ask

Does span have textContent?

Use the textContent property to get the text of a span element, e.g. const text = span. textContent . The textContent property will return the text content of the span and its descendants. If the element is empty, an empty string is returned.

What is wrapper in jQuery?

jQuery wrap() Method The wrap() method wraps specified HTML element(s) around each selected element.


2 Answers

First, you need some way of accessing the paragraph. You might want to give it an id attribute, such as "foo":

<p id="foo">Lorem Ipsum <a href="#">Link</a> <div ... </div> </p>

Then, you can use document.getElementById to access that element and replace its children as required:

var p = document.getElementById('foo'),
    firstTextNode = p.firstChild,
    newSpan = document.createElement('span');

// Append "Lorem Ipsum" text to new span:
newSpan.appendChild( document.createTextNode(firstTextNode.nodeValue) );

// Replace old text node with new span:
p.replaceChild( newSpan, firstTextNode );

To make it more reliable, you might want to call p.normalize() before accessing the first child, to ensure that all text nodes before the anchor are merged as one.


Oook, So you want to replace a part of a text node with an element. Here's how I'd do it:

function giveMeDOM(html) {

    var div = document.createElement('div'),
        frag = document.createDocumentFragment();

    div.innerHTML = html;

    while (div.firstChild) {
        frag.appendChild( div.firstChild );
    }

    return frag;
}

var p = document.getElementById('foo'),
    firstChild = p.firstChild;

// Merge adjacent text nodes:
p.normalize();

// Get new DOM structure:
var newStructure = giveMeDOM( firstChild.nodeValue.replace(/Lorem Ipsum/i, '<span>$&</span>') );

// Replace first child with new DOM structure:
p.replaceChild( newStructure, firstChild );

Working with nodes at the low level is a bit of a nasty situation to be in; especially without any abstraction to help you out. I've tried to retain a sense of normality by creating a DOM node out of an HTML string produced from the replaced "Lorem Ipsum" phrase. Purists probably don't like this solution, but I find it perfectly suitable.


EDIT: Now using a document fragment! Thanks Crescent Fresh!

like image 106
James Avatar answered Nov 03 '22 11:11

James


UPDATE: The method below will search through the subtree headed by container and wrap all instances of textin a span element. The words can occur anywhere within a text node, and the text node can occur at any position in the subtree.

(OK, so it took more than a few minor tweaks. :P)

function wrapText(container, text) {
  // Construct a regular expression that matches text at the start or end of a string or surrounded by non-word characters.
  // Escape any special regex characters in text.
  var textRE = new RegExp('(^|\\W)' + text.replace(/[\\^$*+.?[\]{}()|]/, '\\$&') + '($|\\W)', 'm');
  var nodeText;
  var nodeStack = [];

  // Remove empty text nodes and combine adjacent text nodes.
  container.normalize();

  // Iterate through the container's child elements, looking for text nodes.
  var curNode = container.firstChild;

  while (curNode != null) {
    if (curNode.nodeType == Node.TEXT_NODE) {
      // Get node text in a cross-browser compatible fashion.
      if (typeof curNode.textContent == 'string')
        nodeText = curNode.textContent;
      else
        nodeText = curNode.innerText;

      // Use a regular expression to check if this text node contains the target text.
      var match = textRE.exec(nodeText);
      if (match != null) {
        // Create a document fragment to hold the new nodes.
        var fragment = document.createDocumentFragment();

        // Create a new text node for any preceding text.
        if (match.index > 0)
          fragment.appendChild(document.createTextNode(match.input.substr(0, match.index)));

        // Create the wrapper span and add the matched text to it.
        var spanNode = document.createElement('span');
        spanNode.appendChild(document.createTextNode(match[0]));
        fragment.appendChild(spanNode);

        // Create a new text node for any following text.
        if (match.index + match[0].length < match.input.length)
          fragment.appendChild(document.createTextNode(match.input.substr(match.index + match[0].length)));

        // Replace the existing text node with the fragment.
        curNode.parentNode.replaceChild(fragment, curNode);

        curNode = spanNode;
      }
    } else if (curNode.nodeType == Node.ELEMENT_NODE && curNode.firstChild != null) {
      nodeStack.push(curNode);
      curNode = curNode.firstChild;
      // Skip the normal node advancement code.
      continue;
    }

    // If there's no more siblings at this level, pop back up the stack until we find one.
    while (curNode != null && curNode.nextSibling == null)
      curNode = nodeStack.pop();

    // If curNode is null, that means we've completed our scan of the DOM tree.
    // If not, we need to advance to the next sibling.
    if (curNode != null)
      curNode = curNode.nextSibling;
  }
}
like image 28
Annabelle Avatar answered Nov 03 '22 11:11

Annabelle