Använd javascript för att markera besökarens sökord
Förord
Har du någonsin klickat vidare från en sökmaskin och letat efter orden du sökte på? Skulle det inte varit trevligt ifall sidan du hamnar på markerade tydligt just dom här orden?Innehåll
»»
»
»
»
»
»
»
Relaterade artiklar
» Introduktion till XmlHttpHar du någonsin klickat vidare från en sökmaskin och letat efter orden du sökte på?
Skulle det inte varit trevligt ifall sidan du hamnar på markerade tydligt just dom här orden?
Detta är fullt möjligt att åstadkomma, utan speciellt mycket extra kod, dessutom på ett sådant sätt att ifall användarens browser klarar det, så ser sidan ut precis som om inget extra fanns där.
Om man vill märka ut nånting i en sida när man kodar den så taggar man ju det man vill visa på nåt speciellt sätt, t.ex. genom att sätta en tagg omkring ordet och sätta bakgrundsfärgen till något lämpligt. Problemet här är ju då att vi inte vet när vi skriver sidan vad vi ska märka ut, och inte vill vi sätta en massa extra taggar runt varenda ord heller.
Med hjälp av javascript kan vi via document.referrer kolla varifrån besökaren kom från. Ett litet aber är ju att alla browsers inte sätter document.referrer, men det får vi bara leva med. Alltså, om vi
kommer från en sökmotor, så kommer sökorden att finnas i query strängen och vi behöver bara plocka fram dom och använda dom. Det vettiga vi vill göra är alltså att dynamiskt ändra på sidan så att sökorden får en tagg omkring sej med passlig klass så att vi kan styla med hjälp av CSS. Dessutom vill vi sätta till en ruta som förklarar varför några ord är färgade (eller hur vi nu har stylat dom).
Det här är möjligt genom att manipulera DOM trädet för sidan. Ifall det här med DOM låter obekant, kan det vara fördelaktigt att läsa in sej på det ämnet lite först, eftersom jag inte kommer att gå in speciellt djupt på det i den här artikeln. (Jag har delvis behandlat DOM i artikeln Introduktion till XmlHttp)
Vi börjar med en detalj: Hur testar en sån här sak då, det är ju ändå rätt sällan allt går rätt på första gången, att sätta upp sidan å vänta tills google hittar den är inte vettigt och att sätta upp flera sidor med länkar emellan bara för att få referrern rätt är rätt jobbigt.
Därför har jag gjort så att jag använder ett "magic word" (kreativt nog benämnt testmode) i querysträngen för att aktivera ett testläge.
I testläget kan jag ge querysträngen från samma sida.
Som de flesta vet, så finns all text i textnoder i DOM trädet, det betyder att vi endast behöver gå igenom element av typen textnod och leta efter sökorden. Ifall vi hittar ordet, får vi lov att skapa en ny textnod som innehåller texten fram till sökordet, ett nytt span element med lämpligt klassattribut innehållande sökordet, samt en till textnod som innehåller texten efter sökordet. Dessa tre noder sätter vi in i trädet innan den gamla textnoden, och sedan kan vi ta bort den gamla textnoden. Det här låter komplicerat, men det är det egentligen inte, vi ska titta lite på koden.
Vi börjar med koden för att märka upp ett enskilt ord:
Alltså, först kollar vi igenom barn elementen ifall vi hittar nåt att markera, och vi behöver endast titta på noder av typen text, eftersom det är där allt innehåll finns. För att vi ska kunna matcha oberoende av case, gör vi om allt till gemener och jämför med dem. Sen skapar vi tre nya textnoder, en för texten före ordet, en för ordet och en för texten efter ordet. Därefter skapar vi ett span element med klassen searchword och sätter in ordet i den. Sedan sätter vi in alla tre elementen före den gamla texten, och sedan kan vi ta bort det gamla innehållet.
Nu tänker någon kanske "Hmm, men det där skiter ju sej ifall ordet finns fler än en gång i en textnod.." Så tänkte även jag i ett skede, men det fina med kråksången här är det, att efter som vi traverserar DOM trädet depth-first, så kommer det här fallet att lösa sej automagiskt utan att vi behöver bry oss. (Däremot måste vi kolla så vi inte markerar samma ord flera gånger)
Därefter kan vi titta på koden för att visa info om vad vi sysslar med:
Sök efter container elementet och avbryt om vi inte hittar det, annars sätter vi klassen welcomesearch på containern. Sen skapar vi bara infotexten och sätter till den.
Huvudfunktionen gräver fram sökorden ur querysträngen och markerar dom, samt visar infotexten:
Vi avbryter om vi inte hittar nån querystring i referrern (eller i sidans querystring i testmode). Annars får vi börja bryta upp kodningen, paren separeras av & tecken, nyckel och värde av = tecken. Nycklarna vi är intresserade av är främst "q", och eventuellt använder nån "query". När vi hittat rätt nyckel måste vi unescape:a värdet. Då vi kommit så långt är det dax att visa infotexten om vad som händer och markera alla sökorden skilt för sej.
Och till sist laddar vi koden när sidan är laddad
Normalt när man vill att nåt ska hända när sidan är laddad så krokar man onload händelsen
typ så här:
Skulle det inte varit trevligt ifall sidan du hamnar på markerade tydligt just dom här orden?
Detta är fullt möjligt att åstadkomma, utan speciellt mycket extra kod, dessutom på ett sådant sätt att ifall användarens browser klarar det, så ser sidan ut precis som om inget extra fanns där.
Idé
Om man vill märka ut nånting i en sida när man kodar den så taggar man ju det man vill visa på nåt speciellt sätt, t.ex. genom att sätta en tagg omkring ordet och sätta bakgrundsfärgen till något lämpligt. Problemet här är ju då att vi inte vet när vi skriver sidan vad vi ska märka ut, och inte vill vi sätta en massa extra taggar runt varenda ord heller. Med hjälp av javascript kan vi via document.referrer kolla varifrån besökaren kom från. Ett litet aber är ju att alla browsers inte sätter document.referrer, men det får vi bara leva med. Alltså, om vi
kommer från en sökmotor, så kommer sökorden att finnas i query strängen och vi behöver bara plocka fram dom och använda dom. Det vettiga vi vill göra är alltså att dynamiskt ändra på sidan så att sökorden får en tagg omkring sej med passlig klass så att vi kan styla med hjälp av CSS. Dessutom vill vi sätta till en ruta som förklarar varför några ord är färgade (eller hur vi nu har stylat dom).
Hur gå tillväga?
Det här är möjligt genom att manipulera DOM trädet för sidan. Ifall det här med DOM låter obekant, kan det vara fördelaktigt att läsa in sej på det ämnet lite först, eftersom jag inte kommer att gå in speciellt djupt på det i den här artikeln. (Jag har delvis behandlat DOM i artikeln Introduktion till XmlHttp)
Utveckling och testning
Vi börjar med en detalj: Hur testar en sån här sak då, det är ju ändå rätt sällan allt går rätt på första gången, att sätta upp sidan å vänta tills google hittar den är inte vettigt och att sätta upp flera sidor med länkar emellan bara för att få referrern rätt är rätt jobbigt.Därför har jag gjort så att jag använder ett "magic word" (kreativt nog benämnt testmode) i querysträngen för att aktivera ett testläge.
I testläget kan jag ge querysträngen från samma sida.
...
var referrer = document.referrer;
if (referrer.indexOf("?") == -1) {
// no querystring..
// fake one for local testing
referrer = "" + document.location;
// we want a magic word in querystring to enable testing
if (referrer.indexOf("?testmode") == -1) return;
...
Som de flesta vet, så finns all text i textnoder i DOM trädet, det betyder att vi endast behöver gå igenom element av typen textnod och leta efter sökorden. Ifall vi hittar ordet, får vi lov att skapa en ny textnod som innehåller texten fram till sökordet, ett nytt span element med lämpligt klassattribut innehållande sökordet, samt en till textnod som innehåller texten efter sökordet. Dessa tre noder sätter vi in i trädet innan den gamla textnoden, och sedan kan vi ta bort den gamla textnoden. Det här låter komplicerat, men det är det egentligen inte, vi ska titta lite på koden.
Vi börjar med koden för att märka upp ett enskilt ord:
function markupWord(node, word, colorIdx) {
// depth-first, so first recurse down into children
if (node.hasChildNodes) {
for (var i=0;i markupWord(node.childNodes[i], word, colorIdx);
}
}
// massage this node (if needed)
if (node.nodeType == 3) { // text node
// lowercase both content and word to get a caseinsensitive match
var lowercasedContent = node.nodeValue.toLowerCase();
var lowercasedWord = word.toLowerCase();
// can we find our word in this text blob ?
if (lowercasedContent.indexOf(lowercasedWord) != -1) {
// yep, it's there
/* check that we haven't massaged this one before,
* (due to insertions in the DOM we might see the same node twice)
*/
if (node.parentNode.className.indexOf("searchword") == -1) {
// no, we need to fix it
// need to insert around the word
var startIndex = lowercasedContent.indexOf(lowercasedWord);
// create a text node for the content before the word
var preContent = document.createTextNode(node.nodeValue.substr(0, startIndex));
// create a node for the word
var searchWord = document.createTextNode(node.nodeValue.substr(startIndex, word.length));
// create a node for the content after the word
var postContent = document.createTextNode(node.nodeValue.substr(startIndex + word.length));
// create the span around the word
var highlight = document.createElement("span");
highlight.className = "searchword" + colorIdx;
highlight.appendChild(searchWord);
//insert the new nodes before the old one
node.parentNode.insertBefore(preContent, node);
node.parentNode.insertBefore(highlight, node);
node.parentNode.insertBefore(postContent, node);
//remove the old content
node.parentNode.removeChild(node);
}
}
}
} //markupWord
Alltså, först kollar vi igenom barn elementen ifall vi hittar nåt att markera, och vi behöver endast titta på noder av typen text, eftersom det är där allt innehåll finns. För att vi ska kunna matcha oberoende av case, gör vi om allt till gemener och jämför med dem. Sen skapar vi tre nya textnoder, en för texten före ordet, en för ordet och en för texten efter ordet. Därefter skapar vi ett span element med klassen searchword
Därefter kan vi titta på koden för att visa info om vad vi sysslar med:
function welcome(words) {
var container = document.getElementById("welcomesearch");
// abort unless we have a container
if (!container) return;
// add the "hifromsearch" class to the element
container.className = "welcomesearch";
var p = document.createElement("p");
container.appendChild(p);
// build up the text
var text = "Welcome! You seem to have come here via a search engine.";
if (words.length == 1) {
text += "The word you searched for ("+words[0]+") has been highlighted on this page.";
} else {
text += "The words you searched for ("+
words.join(" ") + ") have been highlighted on this page";
}
// insert the content
p.appendChild(document.createTextNode(text));
}
Sök efter container elementet och avbryt om vi inte hittar det, annars sätter vi klassen welcomesearch på containern. Sen skapar vi bara infotexten och sätter till den.
Huvudfunktionen gräver fram sökorden ur querysträngen och markerar dom, samt visar infotexten:
function markupSearchWords() {
// abort in too old browser
if (!document.createElement) return;
var referrer = document.referrer;
if (referrer.indexOf("?") == -1) {
// no querystring in referrer
// fake one for local testing
referrer = "" + document.location;
// we want a magic word in querystring to enable testing
if (referrer.indexOf("?testmode") == -1) return;
}
// fish out possible search words
// first get query string, everything after ?
var queryString = referrer.substr(referrer.indexOf("?")+1);
//split up query string into key=val pairs
var keyValArr = queryString.split("&");
for(var i=0;i var pair = keyValArr[i].split("=");
// no need to keep digging this key if we do not have any value
if (pair.length == 1) continue;
var key = pair[0];
var value = pair[1];
// google (and a few others) uses q=, someone might use query=
if (key.match(/\bquery|q\b/)) {
// change + to a space
value = value.replace(/\+/g, " ");
// unescape and split at whitespace
var words = unescape(value).split(/\s+/);
// ok, now we have the words, let's do something about them
// put a note about what we've done
welcome(words);
// markup each of the words
for (var w=0;w markupWord(document.getElementsByTagName("body")[0],
words[w], w % NO_OF_CLASSES);
}
// we're done
break;
}
}
} //markupSearchWords
Vi avbryter om vi inte hittar nån querystring i referrern (eller i sidans querystring i testmode). Annars får vi börja bryta upp kodningen, paren separeras av & tecken, nyckel och värde av = tecken. Nycklarna vi är intresserade av är främst "q", och eventuellt använder nån "query". När vi hittat rätt nyckel måste vi unescape:a värdet. Då vi kommit så långt är det dax att visa infotexten om vad som händer och markera alla sökorden skilt för sej.
Och till sist laddar vi koden när sidan är laddad
addLoadEvent(markupSearchWords);
Angående att kroka onload
Normalt när man vill att nåt ska hända när sidan är laddad så krokar man onload händelsentyp så här:
...
...