1

ok i have difficulty to understand the rejex and how it work, I try to do a basic dictionnary/glossary for a web site and i past too many time on it already.

There my code :

// MY MULTIPLE ARRAY
var myDictionnary = new Object();
myDictionnary.myDefinition = new Array();

myDictionnary.myDefinition.push({'term':"*1", 'definition':"My description here"});
myDictionnary.myDefinition.push({'term':"word", 'definition':"My description here"});
myDictionnary.myDefinition.push({'term':"my dog doesn't move", 'definition':"My description here"});

// MY FUNCTION
$.each(myDictionnary.myDefinition, function(){
    var myContent = $('#content_right').html();
    var myTerm = this.term;
    var myRegTerm = new RegExp(myTerm,'g');

    $('#content_right').html(myContent.replace(myRegTerm,'<span class="tooltip" title="'+this.definition+'"> *'+this.term+'</span>'));
});

I create my array and for each result i search in my div#content_right for the same content and replace it by span with title and tooltip. I put my regex empty to not confuse with what i have try before.

In my array 'term' you can see what kind of text i will research. It work for searching normal text like 'word'.

But for the regular expression like 'asterix' it bug, when i find a way to past it, he add it to text, i try many way to interpret my 'asterix' like a normal caracter but it doesn't work.

There what i want : Interpret the variable literally whatever the text inside my var 'myTerm'. It this possible? if it not, what kind solution i shouldn use.

Thank in advance, P.S. sorry for my poor english...(im french) Alex

saitansky
  • 13
  • 3

2 Answers2

1

* is a special character in a Regex, meanining "0 or more of the previous character". Since it's the first character, it is invalid and you get an error.

Consider implementing preg_quote from PHPJS to escape such special characters and make them valid for use in a regex.


HOWEVER, the way you are doing this is extremely bad. Not only because you're using innerHTML multiple times, but because what if you come across something like this?

Blah blah blah <img src="blah/word/blah.png" />

Your resulting output would be:

Blah blah blah <img src="blah/<span class="tooltip" title="My description here"> *word</span>/blah.png" />

The only real way to do what you're attempting is to specifically scan through the text nodes, then use .splitText() on the text node to extract the word match, then put that into its own <span> tag.


Here's an example implementation of the above explanation:

function definitions(rootnode) {
    if( !rootnode) rootnode = document.body;
    var dictionary = [
        {"term":"*1", "definition":"My description here"},
        {"term":"word", "definition":"My description here"},
        {"term":"my dog doesn't move", "definition":"My description here"}
    ], dl = dictionary.length, recurse = function(node) {
        var c = node.childNodes, cl = c.length, i, j, s, io;
        for( i=0; i<cl; i++) {
            if( c[i].nodeType == 1) { // ELEMENT NODE
                if( c[i].className.match(/\btooltip\b/)) continue;
                // ^ Exclude elements that are already tooltips
                recurse(c[i]);
                continue;
            }
            if( c[i].nodeType == 3) { // TEXT NODE
                for( j=0; j<dl; j++) {
                    if( (io = c[i].nodeValue.indexOf(dictionary[j].term)) > -1) {
                        c[i].splitText(io+dictionary[j].term.length);
                        c[i].splitText(io);
                        cl += 2; // we created two new nodes
                        i++; // go to next sibling, the matched word
                        s = document.createElement('span');
                        s.className = "tooltip";
                        s.title = dictionary[j].definition;
                        node.insertBefore(s,c[i]);
                        i++;
                        s.appendChild(c[i]); // place the text node inside the span
                        // Note that now when i++ it will point to the tail,
                        // so multiple matches in the same node are possible.
                    }
                }
            }
        }
    };
    recurse(rootnode);
}

Now you can call definitions() to replace all matches in the document, or something like definitions(document.getElementById("myelement")) to only replace matches in a certain container.

Niet the Dark Absol
  • 301,028
  • 70
  • 427
  • 540
  • Thank you so much, It work like charm now i will try to understand everything but your explanation give me a good idea. I see at some place it not a good practice to use innerHTML but i don't find another way (i know) to do it. I really need to better understand my javascript :P Regards, Alex – saitansky Feb 28 '13 at 18:41
  • I recently got a problem, it seems it dont take all the elements, for exemple (it take the *CSST but not the *INT before and not the second *CSST) somoene know what can cause this problem. Beacause it work but in some case he dont get them all. If i change the place of the element (*LNT before *CSST) in my dictionnari, i get the *LNT but dont get the second *CSST. And if i put my element into a (span) in my text, it take it without problem. But without the span it dont take it at all. – saitansky May 22 '13 at 17:48
0

By adding another variable, it will fix the problem:

            for( j=0; j<dl; j++) {
                  debutI =i;
                  if((io =c[i].nodeValue.indexOf(dictionary[j].term)) != -1) {
                        c[i].splitText(io+dictionary[j].term.length);
                        c[i].splitText(io);
                        cl += 2; // we created two new nodes
                        i++; // go to next sibling, the matched word
                        s = document.createElement('span');
                        s.className = "tooltip";
                        s.title = dictionary[j].definition;
                        node.insertBefore(s,c[i]);
                        i++;
                        s.appendChild(c[i]); // place the text node inside the span
                        // Note that now when i++ it will point to the tail,
                        // so multiple matches in the same node are possible.
                    }
                    i = debutI;
                }

Comme ça, on n'aura pas à mettre tous les spans partout dans le site à la main. >_>

Elaine
  • 1
  • 1