0

How do you highlight (or give any special markup) special characters like ‘’“”‛‟′″˝? Thanks. Using a find and replace .innerHTML.replace() doesn't work as it ruins the event handlers and the DOM.


I've tried the following, but that just ends up with the span's in plain text instead of code.

function highlightText(node, find, rep){
    if(node){
        node= node.firstChild;
        while(node!= null){
            if(node.nodeType== 3){
                node.data= node.data.replace(find, rep);
            }
            else highlightText(node, find, rep);
            node= node.nextSibling;
        }
    }
    return true;
}

highlightText(document.body,/‘/g, "<span style='background: red; color: white;'>‘</span>")
highlightText(document.body,/’/g, "<span style='background: red; color: white;'>’</span>")
highlightText(document.body,/“/g, "<span style='background: red; color: white;'>“</span>")
highlightText(document.body,/”/g, "<span style='background: red; color: white;'>”</span>")
highlightText(document.body,/‛/g, "<span style='background: red; color: white;'>‛</span>")
highlightText(document.body,/‟/g, "<span style='background: red; color: white;'>‟</span>")
highlightText(document.body,/′/g, "<span style='background: red; color: white;'>′</span>")
highlightText(document.body,/″/g, "<span style='background: red; color: white;'>″</span>")
highlightText(document.body,/˝/g, "<span style='background: red; color: white;'>˝</span>")

3 Answers3

3

Here's some code that works in all major browsers. I've posted variations of this code on Stack Overflow before (here, here and here, for example), and made it nice and generic so I (or anyone else) don't have to change it much to reuse it.

jsFiddle example: http://jsfiddle.net/eh8FE/

Code:

// Reusable generic function
function surroundInElement(el, regex, surrounderCreateFunc) {
    // script and style elements are left alone
    if (!/^(script|style)$/.test(el.tagName)) {
        var child = el.lastChild;
        while (child) {
            if (child.nodeType == 1) {
                surroundInElement(child, regex, surrounderCreateFunc);
            } else if (child.nodeType == 3) {
                surroundMatchingText(child, regex, surrounderCreateFunc);
            }
            child = child.previousSibling;
        }
    }
}

// Reusable generic function
function surroundMatchingText(textNode, regex, surrounderCreateFunc) {
    var parent = textNode.parentNode;
    var result, surroundingNode, matchedTextNode, matchLength, matchedText;
    while ( textNode && (result = regex.exec(textNode.data)) ) {
        matchedTextNode = textNode.splitText(result.index);
        matchedText = result[0];
        matchLength = matchedText.length;
        textNode = (matchedTextNode.length > matchLength) ?
            matchedTextNode.splitText(matchLength) : null;
        surroundingNode = surrounderCreateFunc(matchedTextNode.cloneNode(true));
        parent.insertBefore(surroundingNode, matchedTextNode);
        parent.removeChild(matchedTextNode);
    }
}

// This function does the surrounding for every matched piece of text
// and can be customized  to do what you like
function createSpan(matchedTextNode) {
    var el = document.createElement("span");
    el.style.backgroundColor = "red";
    el.style.color = "white";
    el.appendChild(matchedTextNode);
    return el;
}

// The main function
function wrapSpecialChars(container) {
    surroundInElement(container, /[‘’“”‛‟′″˝]+/, createSpan);
}

wrapSpecialChars(document.body);
Community
  • 1
  • 1
Tim Down
  • 318,141
  • 75
  • 454
  • 536
1

The only way to change the formatting of any text in HTML is by defining the style on an HTML element that surrounds the text. So, if you want to highlight only certain characters, those characters will have to be each wrapped with an HTML element, usually a span, though you could maybe be more semantic.

So, I'm not sure what you mean by "ruins event handlers and the DOM", but you are going to have to modify the DOM to highlight characters. There is no way around that.

I understand that if you replace a whole tree of innerHTML, those nodes will be removed and then new ones will be added. However, we can't really give you alternatives without knowing what you are already dealing with.


If you really want to highlight some characters across the whole page, you have to do it non-destructively:

  1. Create a function that operates recursively
  2. Iterate over all child nodes of the node taken as a parameter
  3. If the node is an element, call self recursively with the new node
  4. If the node is a text node, replace that text node with HTML, wrapping the characters in the span you want.
  5. Call the above function with the root node of the content you want to replace.
Nicole
  • 32,841
  • 11
  • 75
  • 101
  • That's not true, for example `.innerHTML.replace()` can replace any random word on the page (including adding a `span`) without the need to change the HTML of the page, which simply isn't accessible. But doing so breaks the rest of the page by destroying the DOM and all event handlers. There has to be a good way to do this. – user793238 Jun 10 '11 at 19:27
  • @user793238 So what's not true? I don't think that contradicts anything I said. I did say that setting innerHTML will replace all the nodes in that part of the tree. – Nicole Jun 10 '11 at 19:32
  • @Renesis you said that the characters should be wrapped in a `span` before that could be done, which isn't true. You can do this even when they aren't, but doing so destroys the DOM and all event handles. Not sure what you're asking for so that you can help. – user793238 Jun 10 '11 at 19:36
  • No, I said they *must* be wrapped in a span *in order to highlight them*. There is no way to highlight them without modifying the DOM. Replacing innerHTML of the whole DOM is not the only way to do it, because yes, that will destroy and rebuild the whole thing. – Nicole Jun 10 '11 at 19:38
  • Okey, so can you help or not? – user793238 Jun 10 '11 at 19:42
  • There are many ways to do what you are asking. I've edited my answer to explain the steps that you'll need to take to do it. You asked how to highlight characters -- the answer to that part is "you have to wrap it in an HTML element" – Nicole Jun 10 '11 at 19:48
  • When do you want to highlight these charachters, on hover over the parent paragraph? – Liam Bailey Jun 10 '11 at 19:59
  • @Liam Bailey: No always, when looking at the page. – user793238 Jun 10 '11 at 20:31
  • I tried the following (see main post, too long for here). But that just ends up with the `span`'s in plain text and not as markup. – user793238 Jun 11 '11 at 08:00
  • This answer is correct, and describes the steps the code in my answer performs. – Tim Down Jun 11 '11 at 11:10
-1

You can use special object function for this. JQuery can help you to do it in a short way.

For example you can change styles of objects to highlight when page is loaded. There is a main idea here.

<body onload='highlight()'>
...
</body>

where

function highlightSpan( content )
{
  return "<span style='background: yellow'>" + content + "</span>";
}

function hightlight()
{
  var highlightChars = "‘’“”‛‟′″˝";
  // change body content (and highlight chars)

  var bodyContent = $( "body" ).html();

  for( var i = 0; i < highlightChars.length; i++ )
  {
     var highlightChar = highlightChars[ i ];
     bodyContent = bodyContent.replace( highlightChar, highlightSpan( highlightChar ) );
  }

  $( "body" ).html( bodyContent );

}

You can use document.getElementsByTagName( "body" )[0].innerHTML instead of $( "body" ).html().

May be replace() will replace only one matching first character. So you can use replace( /(‘|’|“|”|‛|‟|′|″)/g, highlightSpan( something ) ); instead. And solve problem with ~ which is a special regexp character.

sergzach
  • 6,578
  • 7
  • 46
  • 84
  • But it's better when backend forms output with highlighted characters. – sergzach Jun 10 '11 at 20:01
  • The problem with this is the whole DOM is removed and replaced with new nodes. The OP mentioned that was the problem he/she wanted to avoid in the first place. – Nicole Jun 10 '11 at 20:11