2

I have the following HTML structure:

<div class="content">
  <p>somecontent</p>
  <p>another content <span id="name-1">content</span> 1234214</p>
</div>

I want to wrap only numbers in additional span (1234214). So far I've made this:

jQuery(window).load(function() {
  jQuery('.content p').html(function(index, value) {
    return value.replace(/(\d+)/g, '<span class="mathjaxfont">$1</span>');
  });
});

However this replaces the 1 in span id. How can I exclude checking element attributes?

cmbuckley
  • 40,217
  • 9
  • 77
  • 91

3 Answers3

1

You might want not only to exclude attributes (think about the h1-element for example) but constrain your replacing on the text nodes. See this questions for some ideas on how to get only and work with text nodes: How do I select text nodes with jQuery?

This answer in above question How do I select text nodes with jQuery? gives you a collection of text-nodes on which you can do your string-replacing.

Community
  • 1
  • 1
Alexander Haas
  • 278
  • 2
  • 11
1

You should use .contents() and .replaceWith() for this:

jQuery('.content p').contents().each(function() {
    var method = this.nodeType == 1 ? 'html' : 'replaceWith';
    $(this)[method](this.textContent.replace(
        /(\d+)/g,
        '<span class="mathjaxfont">$1</span>'
    ));
});

Here's a JSFiddle.

cmbuckley
  • 40,217
  • 9
  • 77
  • 91
  • This removes the span with id="name-1". – Deyan Kostadinov Sep 16 '14 at 09:16
  • Yes, it doesn't work correctly too... This question is cursed. – Regent Sep 16 '14 at 09:18
  • Nice spot - I had a working version but I'd simplified it and not noticed that it removed the span too. I've updated to use `.html()` for elements and `.replaceWith()` for text nodes. – cmbuckley Sep 16 '14 at 09:55
  • +1 Looks like it works now... Then my hand-made "reinventing the wheel" hard [solution for this question](http://jsfiddle.net/r979Ly8h/4/) deserves only to be a comment. – Regent Sep 16 '14 at 10:16
  • Owh... I found out that it doesn't work on [this example](http://jsfiddle.net/r979Ly8h/5/) :) – Regent Sep 16 '14 at 10:23
  • @Regent yeah, I should have mentioned that nested elements aren't supported, but as your answer shows, solving that problem isn't at all easy! :-) – cmbuckley Sep 16 '14 at 11:17
  • @cbuckley I actually suppose that there is no easy solution because of necessary mixing of `.text()` with `.html()`... And because of possible nested elements. – Regent Sep 16 '14 at 11:19
0

Long and hard solution, but should work in nested elements.

The idea is to handle element's .html() string character by character, wrapping numbers when they are found, but omitting numbers inside tags' definition.

Fiddle.

$(document).ready(function()
{
    $('.content p').each(function()
    {
        $(this).html(handleHtml($(this).html()));
    });
});

function handleHtml(html)
{
    var resultHtml = "";
    var numberStr = "";
    var re = /[0-9]/;
    var isTag = false, quote = "";
    for (var i = 0; i < html.length; i++)
    {
        var char = html.substr(i, 1);
        if (!isTag && re.test(char))
        {
            numberStr += char;
        }
        else
        { 
            if (numberStr)
            {
                resultHtml += wrapNumber(numberStr);
                numberStr = "";
            }
            resultHtml += char;
            if (isTag && !quote && (char == '"' || char == "'"))
            {
                quote = char;
            }
            else if (quote && quote == char)
            {
                quote = "";
            }
            if (char == '<')
            {
                isTag = true;
            }
            else if (!quote && char == '>')
            {
                isTag = false;
            }
        }
    }
    if (numberStr)
    {
        resultHtml += wrapNumber(numberStr);
    }
    return resultHtml;
}

function wrapNumber(number)
{
    return '<span class="mathjaxfont">' + number+ "</span>";
}
Regent
  • 5,142
  • 3
  • 21
  • 35