0

I'm using this script to wrap a specific word with span tags. I need to exclude the replace though if the the word is an href attribute.

// Prevent orphaned words for strings with more than 3 words
$("p:not(.form-row),h1,h2,h3,h4,h5,h6").each(function (i, e) {
    var text = $(e).html();
    text = text.trim().split(' ');
    if (text.length > 3) {
        var lastWord = text.pop();
        text = text.join(' ') + " " + lastWord;
        $(e).html(text);
    }

    // Wrap all occurrences of word-123 with a span so it doesn't break to two lines
    $(e).html( $(e).html().replace(/word-123/ig, '<span class="nowrap">$&</span>') );

}); 

When the word is in an href, the word in the href is getting wrapped breaking the href. So

Lorem ipsum dolor sit amet, <a href="word-123">consectetur</a> adipiscing elit.

Is rendering as

Lorem ipsum dolor sit amet, <a href="<span class="nowrap">word-123</span>">consectetur</a> adipiscing elit.
Junky
  • 958
  • 7
  • 17
  • 1
    Why do you event take HTML to start with? `var text = $(e).html();` Just use `.text()` .. and here DO insert it as html: `$(e).html(text);` . That will live out href attribute and any other HTML attribute for that matter when you make your span out of it. – ikiK Feb 26 '21 at 16:29
  • Also: `:not(a[href])` – ikiK Feb 26 '21 at 16:36
  • Does this answer your question? [jQuery: Find text and replace with HTML](https://stackoverflow.com/questions/23759703/jquery-find-text-and-replace-with-html) – D M Feb 26 '21 at 16:41
  • Replacing var text = $(e).html() with .text() removes the link completely. – Junky Feb 26 '21 at 16:43
  • Someone recently provided an answer to their own question for exactly this - go and give them an upvote: https://stackoverflow.com/a/66372448/2181514 – freedomn-m Feb 26 '21 at 16:48
  • i'm not sure how to apply that script to my situation so any help would be appreciated here. – Junky Feb 26 '21 at 16:52
  • Provide the HTML and the `nowrap` CSS. Your question looks like an [XY problem](https://meta.stackexchange.com/a/66378/941514). – Louys Patrice Bessette Feb 26 '21 at 16:56
  • Does this answer your question? [How to wrap each letter in a span and keep normal word breaks and subelements](https://stackoverflow.com/questions/66372447/how-to-wrap-each-letter-in-a-span-and-keep-normal-word-breaks-and-subelements) – freedomn-m Mar 01 '21 at 09:18

1 Answers1

0

As mentioned in the comments below your question, I've made a similar solution a couple days ago.

The trick is to use jQuery's contents() to get the different types of nodes (element or text nodes) and threat them differently. I use jQuery's clone to clone the wrapping element for each word inside the sub element.

As also mentioned in my post, the solution has two known issues:

  • Only one level of sub-elements works (So <p>Hello <strong><u>W</u>orld</strong></p> won't)
  • Text against another element create two separate words (So <p>User<strong>name</strong></p> are two words)

Those should be fixable.

I've modified it a bit for you're use case:

$.fn.convertToSeperateWords = function() {
  return this.each(function() {
    var $el = $(this);
    var elements = convertToSeperateWords($el, false);

    $el.empty().append(elements);

    return $el;
  });
}

$('p').convertToSeperateWords();

function convertToSeperateWords($element, asSubNode) {
  var elements = [];

  var childNodes = $element.contents();

  // Loop through all child nodes of selected element
  for (var c = 0; c < childNodes.length; c++) {
    var node = childNodes[c];
    var type = node.nodeType;

    // Process a child element
    if (type == Node.ELEMENT_NODE) {
      Array.prototype.push.apply(elements, convertToSeperateWords($(node), true));
    }

    // Process a piece of text
    else if (type == Node.TEXT_NODE) {
      var text = node.nodeValue;

      // Process each word
      var words = text.split(' ');
      for (var w = 0; w < words.length; w++) {
        var word = words[w];

        // Skip empty words
        if (word == '') continue;

        // Wrap each word into span
        var $word = $('<span/>').addClass('word');

        if (!asSubNode) {
          $word.html(word);
          elements.push($word);
        }

        if (asSubNode) {
          var $subNode = $element.clone().empty().html(word).addClass('word');
          elements.push($subNode);
        }
      }
    }
  }
  return elements;
}
p,
.word {
  padding: 3px 1px;
}

p {
  border: 1px solid red;
}

.word {
  display: inline-block;
  border: 1px solid green;
  margin-left: 2px;
  margin-right: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Lorem ipsum dolor sit amet, <a href="word-123">consectetur word2</a> adipiscing elit.</p>
Andreas Furster
  • 1,558
  • 1
  • 12
  • 28