Att utveckla JavaScript är en balansgång på brinnande lina. JavaScript kan göra fantastiska skillnader i användbarhet, och ingen ska vara rädd för att neka sina webbapplikationer sådant. Samtidigt är kraven på kvalitet högre än någonsin: Genvägar kan göra googles spindlar ledsna, script som inte fungerar i vissa webbläsare skapar frustration och missämja, och långa laddningstider är det aldrig någon som gillat.
Jag tänkte därför mycket kort skriva ner mina JavaScript-vanor, i hopp om att det hjälper någon.
Först: hur det inte ska se ut¶
När JavaScript googlas på innehåller många nybörjartutorials nedanstående exempel.
Tjohej!
Tjohej!
<form action="" onsubmit="foobar()"> ... </a>
<input type="text" onchange="foobar()">
// och så vidare
Det är sorgligt att sådana dåliga exempel ska synas mest för alla som vill lära sig göra häftiga webbplatser. Länkar med felaktigt href
(protokollet javascript
existerar inte) gör googles spindlar förvirrade och kan påverka sökplaceringar i större omfattningar. Bland webbutvecklare är det sett som dålig sed att använda style
-attributet på HTML-element, för att det skapar spaghettikod och blir svårare att hantera i långa loppet. Av samma skäl hör varken onclick
, onchange
eller liknande hemma i HTMLdokument.
Lugn, det finns bättre!¶
Det jag talar om är en filosofi som heter Unobtrusive JavaScript, som grovt går ut på att gränssnitt har tre lager:
Semantik och disposition av innehåll : HTML och XHTML
Presentation och design : CSS
Interaktivitet och respons : JavaScript
JavaScript är ett lager i sig självt, som ska arbeta fristående från de andra två (undre) lagren. Rent praktiskt ser det ut så här:
window.onload = function()
{
var lnk = document.getElementsByTagName('a');
lnk.onclick = foobar;
var frm = document.getElementsByTagName('form');
frm.onsubmit = foobar;
var inpt = document.getElementsByTagName('input');
inpt.onchange = foobar;
}
Det enda spåret vi ser av detta är en script
i head
:
<html>
<head>
<title>Exempelsida</title>
...
<script src="foobar.js" type="text/javascript"></script>
</head>
...
<html>
Hemligheten ligger i window.onload
. Den körs varje gång en sida har laddats. Det ovanstående kod gör är att leta upp HTML-element via DOM och tilldela dessa händelselyssnare. Det finns otaliga bättre sätt att göra det på än jag gör, men principen är densamma: JavaScript initieras av JavaScript, ej av HTML.
Tjästestrukturerade script¶
Med ovanstående i åtanke kan jag gå vidare med hur jag brukar göra för att enkelt kunna hitta i mina (till slut gigantiska) JavaScript-filer. Jag börjar alltid med att skissa på vad jag ska scripta, och delar in dessa i tjänster och delade egenskaper.
Tjänst : Kan vara en blogg, kontaktsida, kalender eller något annat som bildar en del av ett dokument.
Delade egenskaper : Kan vara toggle-funktioner, tooltips, formulärwidgets eller annat som delas av flera tjänster.
Utifrån det använder jag JavaScriptobjekt för att skapa egna scopes. Ett typiskt objekt brukar se ut så här för mig:
cn (objekt) : Står för "classnames", men innebär mer än så. Här skriver jag upp alla klassnamn, id och elementnamn som jag behöver för att söka igenom DOM-trädet. När jag vill ha bloggens wrapper (omslutande element) skriver jag this.cn.wrp
eller Blog.cn.wrp
.
labels (objekt) : Om jag skapar nya element med script, ska länktexter, knapptexter et cetera stå här för att underlätta hantering.
Exists (function : bool) : Funktionen letar igenom DOM-trädet efter instanser av tjänsten, och returnerar en bool (sant/falskt).
init (function : void) : Applicerar script på tjänsten.
var Blog = new Object();
Blog.cn = new Object();
Blog.cn.wrp = "blogWrapper";
Blog.cn.wrp = "blogEntries";
Blog.labels = new Object();
Blog.labels.toggle = "visa hela";
Blog.exists = function(){ //finns? };
Blog.init = function(){ //starta };
Jag föredrar dock istället kortvarianten för att det är mer läsbart:
var Blog = {
cn : {
wrp : "blogWrapper",
entry : "blogEntries"
},
labels : {
toggle : "visa hela"
}
exists : function(){
//finns?
},
init : function(){
//starta
}
}
Till detta kompletterar jag med en funktion som brukar ha "prepare" som prefix, en sk invoke:
window.onload = function() {
prepareBlog();
};
function prepareBlog() {
if(Blog.exists()) {
Blog.init();
}
}
I den anonyma funktionen läggs alla tjänsters invokes efter varandra för att exekvera på sidan. Ett annat sätt är att låta Blog.init
köra Blog.exists
internt och avbryta om falskt istället för att skapa en ny funktion för det, men det är som man själv vill.
Nog för idag¶
Jag är inte riktigt färdig ännu. Det återstår litet pill till innan jag är klar med mina rutiner. Mer om detta kommer i en senare blogg om ämnet.
Vidare läsning
- Progressive Enhancement och Graceful Degradation.
- DOM scripting av Jeremy Keith, en bok om javascript på rätt sätt.
- Simply JavaScript av Cameron Adams och Kevin Yank, en annan bok om javascript på rätt sätt.