After a day's coding, I have come up with this. It's closer to what I need but the final link creation phase isn't there ye.t (also each link will have attributes that point to a different URL, different event handlers, etc, so don't think they're only going to have a jargonbuser class and nothing else)
var jargonEnabled = true; var disableCaching = true; var jDivOffsetX = -10; var jDivOffsetY = 12; var targetId = 'content'; var regexOptions = 'gi'; function newJargonEntry (xmlNode) // Create an entry for this item in the jargonTerms array { var newTerm = null; if (thisDesc = xmlNode.getElementsByTagName ('description')[0]) { newTerm = new Object (); newTerm ['term'] = xmlNode.getAttribute ('term'); newTerm ['href'] = xmlNode.getAttribute ('href'); newTerm ['description'] = thisDesc.firstChild.data; } return (newTerm); } function insertJargonLinks (thisNode, thisTerm) { switch (thisNode.nodeName.toLowerCase ()) { // If this node is plain text then do search and replace on it case ('#text') : temp = thisNode.data; temp = temp.replace (thisTerm, '<a class="jargonBuster">$1</a>'); thisNode.data = temp; // This only spits the raw HTML into the textnode, which is not what we really want but will do for testing. break; // If this node is one of the following types then skip it case ('!') : // IE 5.5 case ('#cdata-section') : case ('#comment') : case ('#document') : case ('#document-fragment') : case ('a') : case ('button') : case ('h1') : case ('h2') : case ('option') : case ('script') : case ('select') : case ('style') : case ('textarea') : break; // If this node has children then process them recursivly default : if (thisNode.hasChildNodes ()) { $(thisNode.childNodes).each (function () {insertJargonLinks (this, thisTerm);}); } break; } } function insertJargon (xmlObject) // Insert Jargon hyperlinks into content div { // Parse the returned XML if (xmlObject.getElementsByTagName('jargon')[0]) { xmlRoot = xmlObject.documentElement; // Get the jargon terms if (xmlTerms = xmlRoot.getElementsByTagName ('jargonterm')) { // Set up RegExp searchTerm = new RegExp ('', ''); $(xmlTerms).each (function () { // Create a new jargon entry jargonTerms [this.getAttribute ('id')] = newJargonEntry (this); // Prepare keyword search searchTerm.compile ('(' + this.getAttribute ('regex') + ')', regexOptions); // Do search and replace on all the children of the target node $(document.getElementById (targetId).childNodes).each (function () { insertJargonLinks (this, searchTerm); }); }); } } return (false); } function jargonDiv (selectedTerm) // Manage the jargon buster div { if (!document.getElementById ('ajaxNode')) { // Hide the div closeJargonDiv (); // Update div contents ajaxHeadTitle.innerHTML = jargonTerms [selectedTerm]['term']; ajaxNodeText.innerHTML = jargonTerms [selectedTerm]['description']; // Show the div document.body.appendChild (ajaxNode); } return (false); } function closeJargonDiv () // If the div is already visable, then hide it { try { document.body.removeChild (ajaxNode); } catch (e) {} return (false); } function doJargon () // Event handler for AJAX get { $.get (dataSource, function (response) { insertJargon (response); // Attach mousemove handlers to all generated links $('a.jargonbuster').mousemove (function (evt) { $(ajaxNode).css ('left', (evt.pageX + jDivOffsetX)); $(ajaxNode).css ('top', (evt.pageY + jDivOffsetY)); }); return (false); }); } // --------------------------------------------------------------------------------------------- if (jargonEnabled) { // Variable initialization var jargonTerms = new Object (); // Node used for displaying help var ajaxNode = document.createElement ('div'); ajaxNode.id = 'ajaxNode'; // Child divs for jargon node ajaxNodeTitle = document.createElement ('div'); ajaxNodeText = document.createElement ('div'); ajaxNodeTitle.id = 'ajaxNodeTitle'; ajaxNodeText.id = 'ajaxNodeText'; // Help title ajaxHeadTitle = document.createElement ('h3'); ajaxHeadTitle.id = 'ajaxHeadTitle'; // Associate child nodes with the main div ajaxNode.appendChild (ajaxNodeTitle); ajaxNode.appendChild (ajaxNodeText); ajaxNodeTitle.appendChild (ajaxHeadTitle); // Start the search and replace process $(document).ready (doJargon); } The .data field doesn't really work (it injects the text as HTML entities) but it at least illustrates what's meant to happen in a live document. On May 16, 4:01 pm, RobG <[EMAIL PROTECTED]> wrote: > On May 13, 10:28 am, Gordon <[EMAIL PROTECTED]> wrote: > > > I had previously written a javascript that scans a div inside a HTML > > document for keywords and then wraps those words in hyperlinks, as > > part of adynamic help system. This worked by modifying the innerHTML > > of the div in question. This approach worked but had a few problems, > > namely that you had to be careful you didn't accidentally try to wrap > > links around form elements, tag attributes and other such things, and > > that it didn't work in Safari. > > > I did a recode recently that made use of the jQuery library. It cut > > the line count of the script in half but it still worked in > > fundamentally the same way as before, by doing regex search and > > replace on the .html() of the target div. > > I think replacing innerHTML is kludgy. Below is an example that uses > a DOM traversal and replaces matching words within text nodes with > wrapped words and text. It's pretty straight forward, you need to > skip over some nodes (textareas, selects, etc.) that you don't want to > modify. I uses a NodeList and adds nodes to it, so it goes backwards > over it. Tested in Firefox and Safari, I'll leave IE to you. > > I'm new to jQuery so it's not jQuery-ish at all, maybe someone else > can do that bit. > > <script> > > // Recursive traverse of DOM from node down > function highLight(node, txt){ > > // Don't wrap content of textarea, select, option or button elements > // Add any other elements that shouldn't be processed > var tagName = node.tagName.toLowerCase(); > if ('textarea' == tagName || > 'select' == tagName || > 'option' == tagName || > 'button' == tagName ) { > return; > } > > if (node.nodeType == 3) return wrap(node, txt); > > var kid, kids = node.childNodes; > var i = kids.length; > while(i--) { > kid = kids[i]; > (kid.nodeType == 1)? highLight(kid, txt) : wrap(kid, txt); > } > > // Remove unnecessary text nodes > if (node.normalize) node.normalize(); > > } > > function wrap(txtNode, txt) { > > var re = new RegExp('\\b' + txt + '\\b'); > var s = txtNode.data.split(re); > > if (s.length < 2) return; > > var p = txtNode.parentNode; > var lastEl, tempEl; > > // Element to wrap text in, could use em, strong, a, etc. > var wrapEl = document.createElement('span'); > wrapEl.style.color = 'red'; > > lastEl = document.createTextNode(s[0]); > p.replaceChild(lastEl, txtNode); > > for (var i=1, len=s.length; i<len; i++) { > tempEl = wrapEl.cloneNode(true); > tempEl.appendChild(document.createTextNode(txt)); > p.insertBefore(tempEl, lastEl.nextSibling); > p.insertBefore(document.createTextNode(s[i]), tempEl.nextSibling); > lastEl = tempEl.nextSibling; > } > > } > > </script> > </head> > <body> > > <div id="xx">Here is some blah text so that I can blah test > <p>my new blah</p>blah function to see if it blah blah works and joins > blahdy blah > <textarea>Here is blah text</textarea> > <select><option>blah<option>blah</select> > <div>blah</div> > <div>And blah. awsome. blah is blah.</div> > <table><tr><td>blah<td>some blah text<td>blah text blah</table> > </div> > <button onclick=" > highLight(document.getElementById('xx'), 'blah'); > ">Hightlight blah</button> > > </body> > > -- > Rob