En introduktion till Dynamisk HTML (DHTML)

Full text

(1)

? 2002 Ulf Tornert

1

En introduktion till

Dynamisk HTML (DHTML)

(2)

? 2002 Ulf Tornert

2

DHTML – en introduktion ... 3

Vad är DHTML? ... 3

Vad behöver du för att lära dig DHTML?... 3

Document Object Model i Internet Explorer 4 ... 4

Document Object Model i IE5 och NN6... 6

Några enkla exempel... 8

Hit me! (ett skolexempel)... 8

Dolda inforutor i sidan... 10

Pop-up menyer ... 13

Scrolla lager... 16

Animera lager... 20

Övningsuppgifter... 23

(3)

? 2002 Ulf Tornert

3

DHTML – en introduktion

Vad är DHTML?

Egentligen är hela begreppet dynamisk HTML inte särskilt väldefinierat. Den vidaste meningen är förmodligen en HTML som inte är statisk, vad avser antingen sidans innehåll, struktur eller

presentation. HTML har den uppenbara nackdelen att en sidas innehåll och presentation är låst efter det att sidan är laddad, därefter kan man ingenting göra. Som väl är finns det vägar att komma runt detta. Dynamisk HTML förfogar alltså över möjligheten att manipulera med sidans innehåll, struktur och presentation efter det att sidan är laddad. Den typ vi skall titta på behandlar struktur och presentation och inte innehållet. Innehållet är i viss mening statiskt och bestäms vid det tillfälle sidan tillverkas, hur detta innehåll sedan presenteras kan skilja åt mellan olika

plattformar, kan variera beroende på hur en besökare interagerar (för att ta ett slitet ord) med sidan och så vidare. Du kan göra olika element osynliga, du kan flytta omkring dem på skärmen, du kan byta färg på saker och ting etc. Många tror att DHTML är ett eget helt nytt språk, men det är faktiskt inget annat än JavaScript tillsammans med stilmallar (CSS).

En annan form av ”dynamisk HTML” är den som hela tiden söker i en databas för att skapa uppdaterad information i sidan. Nu är det kanske inte struktur och presentation som dynamiken avser, utan det ständigt föränderliga innehållet. Tänk er en bank som visar aktuella börskurser eller liknande. Det skulle vara helt omöjligt (och minst lika tråkigt) att sitta och skriva in alla eventuella förändringar för hand. Hur gör man då? Ja, när en besökare laddar sidan kommer en programsnutt att köras på den aktuella webbservern. Denna kontrollerar de aktuella kurserna i en central databas och dessa värden skickas sedan med sidan till din browser. Det sker helt

automatiskt. Den sida som visar kurserna behöver endast ”fråga databasen” för att få

uppdaterade värden. Således behöver denna sida ingen manuell uppdatering – mycket elegant!

Tyvärr ligger detta långt utöver kursens omfattning.

När Java var nytt kunde man höra många säga att applets skulle revolutionera hela webben, eftersom dessa hade förmågan att ge webbsidor en levande karaktär. Nu när hypen över applets har lagt sig, och andra tekniker får uppmärksamhet, inser de flesta att applets är inte bäst på allt.

Det går att lösa mycket med den enkla DHTML vi skall lära oss här. Valet av teknik handlar förstås först och främst om vilken funktionalitet man vill ha, men ibland lika mycket om nedladdningstid (som vanligt). Man brukar lite slarvigt säga att ”Applets tar lång tid att ladda, men exekverar snabbt. DHTML går snabbt att ladda, men exekverar långsamt”. Det finns saker som applets kan som man inte kan åstadkomma med DHTML, och om du måste ha just den funktionaliteten får du antingen leta upp en fri applet på webben (det finns hur mycket som helst), eller lära dig Java och skapa den själv.

Vad behöver du för att lära dig DHTML?

Den form vi skall lära oss använder stilmallar tillsammans med JavaScript. Det är därför en nödvändighet att du förutom HTML hanterar dessa tekniker ganska bra. Eftersom inga andra tillbehör används är kraven desamma som för att lära dig de tidigare momenten – du behöver en texteditor och en browser som klarar stilmallar och JavaScript. Det innebär som regel Netscape Navigator eller Internet Explorer med versionsnummer 4 eller senare. Dock är det så att Explorer är helt överlägsen Netscape när de tolkar stilmallar (ibland även JavaScript, vilket är pinsamt eftersom JavaScript är en teknik från Netscape). Netscape 4 har alltså rena buggar vad avser stilmallar, men det är inte allt! NN och IE har lite olika varianter av sina objekt modeller (Document Object Model) och dialekter av JavaScript. Microsofts svar på JavaScript heter JScript och dessa skiljer sig åt tillräckligt mycket för att ställa till problem. Det visar sig ibland vara en

(4)

? 2002 Ulf Tornert

4

näst intill omöjlig uppgift att skapa dynamiska sidor som visar sig exakt likadant i de två webbläsarna. Det är därför viktigt för den som vill bli duktig att sätta sig in i vad som fungerar och inte fungerar i de båda. När en sida helt enkelt inte går att göra ”cross-browser” (som man brukar säga när en sida fungerar i både NN och IE) – får man antingen sänka ambitionsnivån eller skapa två helt separata filer, en för vardera läsaren. Inte populärt!

Organisationen W3C – The World Wide Web Consortium – arbetar för att ta fram en enhetlig objekt modell som samtliga aktörer kan enas kring. Det påstås att man lyckats ganska bra i och med NN6, IE5+ (m. fl. exempelvis Opera). Här lämnar jag över till den som vill att undersöka vidare.

Document Object Model i Internet Explorer 4

I momentet JavaScript har du lärt känna Netscapes objekt modell. Den fungerar så att en virtuell avbildning av sidan skapas där alla de ingående objekten finns respresenterade. Mellan dessa objekt råder en alldeles unik hierarki. Om jag skapar ett lager med <layer>-taggen kommer en korrekt referens till dess bakgrundsfärg att vara exempelvis

document.layers[0].bgColor

Om jag dessutom namngett lagret med firstLayer kan jag referera till lagret med satserna

document.firstLayer.bgColor

document.layers[”firstLayer”].bgColor

Om jag har nästlat ett lager inuti det första och jag vill ändra på bakgrundsfärgen i detta andra lager kan jag referera med

document.firstLayer.document.secondLayer.bgColor

Dvs firstLayer har ett document-objekt som skall ingå i referensen! Om jag glömmer detta blir det helt fel. På det här sättet brukar man säga att referenserna är som en ”roadmap” till det aktuella objektet.

Tyvärr fungerar inte IE’s objekt modell likadant, eller kanske är det tvärtom. Jag tycker nog att IE’s modell är enklare och det faktum att de inte överensstämmer är mycket frustrerande för den som vill skapa dynamiska sidor för båda webbläsarna. För det första finns inte <layer>-taggen överhuvudtaget i IE, man skapar lager med <div>-taggen och för det andra är referenserna till ett sådant lager helt annorlunda. Om jag således i IE skapar ett lager och kallar det firstLayer, så är en korrekt referens till egenskapen visibility

document.all[”firstLayer”].style.visibility

Man använder nyckelorden all och style. Om jag nästlar ett lager inuti detta blir referensen

document.all[”secondLayer”].style.visibility

Dvs precis som förut, bara det att namnet på lagret är ändrat! Denna referens påminner inte alls om att lagren är nästlade (vilket faktiskt förenklar allt). För att detta ska fungera måste förstås alla ingående objekt i sidan ha unika namn, men det ska man eftersträva i alla fall. Betrakta följande exempel:

(5)

? 2002 Ulf Tornert

5

som skapades med koden

<html>

<head>

<title>dhtml_1.htm</title>

<script language="JavaScript"><!--

function view(lager, current) {

document.all[lager].style.visibility = current;

}

//--></script>

</head>

<body bgcolor="#FFFFFF" text="#000000">

<div id="firstLayer" style="background-color:yellow; height:100;

width:100; position:absolute; left:20; top:80">

<P>firstLayer...

<div id="secondLayer" style="background-color:blue; height:100;

width:100; position:absolute; left:20; top:20">

<P>secondLayer...

</div>

</div>

<form>

<input type="button" value="Dölj firstLayer!" onClick="view('firstLayer', 'hidden')">

<input type="button" value="Visa firstLayer!" onClick="view('firstLayer', 'visible')"><br>

<input type="button" value="Dölj secondLayer!"

onClick="view('secondLayer', 'hidden')">

<input type="button" value="Visa secondLayer!"

onClick="view('secondLayer', 'visible')">

</form>

</body>

</html>

(6)

? 2002 Ulf Tornert

6

Referenserna i funktionen view() kommer att bli av typen

document.all[“firstLayer”].style.visibility = “hidden”;

eftersom de argument som skickas med anropet är just namnet på de två lagren och värdena

hidden och visible. Lite längre fram skall jag visa hur man kan automatisera referenserna något så att de passar både NN och IE. Åtminstone för lager som inte är nästlade.

En sida som skapar lager med <div>-taggen och som laddas med Netscape kommer att behandlas som om lagren skapades med <layer>-taggen. Dvs Netscape bygger upp en helt likvärdig avbildning och referenserna skall därför göras som vanligt. Två nästlade <div>-taggar som ovan skall ha de respektive referenserna

document.firstLayer.visibility

document.firstLayer.document.secondLayer.visibility

Document Object Model i IE5 och NN6

Under ganska lång tid har webbsidor kompatibla med den fjärde generationens webbläsare varit

”standard” på webben. Problemet har förstås varit att denna standard egentligen inte är någon standard, utan smarta utvecklare hittar på sina egna små knep och tricks för att arbeta sig runt skillnader mellan exempelvis NN4.7 och IE4. W3C-konsortiet (http://www.w3.org) har ju tagit på sig uppgiften att även standardisera DOM för webbläsare och både IE5 (och framåt) NN6 anses vara mycket kompatibla med denna standard. Det är på ett sätt bra, eftersom en standard är åtråvärd och dessutom är det nu enklare att koda dynamisk HTML. På ett annat (ganska alarmerande) sätt är det dåligt, eftersom tidigare skrivna skript och webbsidor fullständigt havererar med den nya dokument-objekt-modellen! Men för att vi inte skall bli akterseglade helt och hållet tar jag här, mycket kortfattat, upp hur man gör referenser till lager i en sida som läses med ovan nämnda webbläsare.

Anta att vi har ett lager med namnet lager1 i en sida. Lagret skapas med en div-tagg. I sidan finns även en knapp, med vilken jag vill styra över lagrets synlighet. Jag skapar en knapp med vilken jag med onClick anropar en JavaScript-funktion.

<html>

<head>

<title>Referenser i IE5 och NN6</title>

<style>

#lager1 {

position:absolute;

width:100px;

height:100px;

top:50;

left:50;

background-color:yellow;

}

</style>

<script>

var showing = true;

function toggleLayer() {

elm = document.getElementById("lager1");

(7)

? 2002 Ulf Tornert

7 if (showing)

{

elm.style.visibility = "hidden";

showing = false;

} else {

elm.style.visibility = "visible";

showing = true;

} }

</script>

</head>

<body bgcolor="#FFFFFF" text="#000000">

<form>

<input type="button" value="Klicka här!" onClick="toggleLayer()">

</form>

<div id="lager1">Här har vi lager1</div>

</body>

</html>

Jag skapar mig en lagerreferens med satsen elm = document.getElementById("lager1");

Därefter kan jag använda samma sätt som i IE4 för att komma åt en style-egenskap:

elm.style.visibility = ”hidden”;. Om jag vill flytta på lagret kan jag skriva

elm.style.pixelTop += 2;. Sidan ser ut så här:

När man klickar på knappen kommer det lilla gula lagret att växelvis släckas och tändas. För att koda optimalt kan det vara intressant att veta ganska ingående vilken browser en besökare har.

Om du verkligen vill avgöra detta kan du använda dig av de kollektionsvariabler (vektorer) som är utmärkande för NN och IE. Till exempel: NN4 använder sig av document.layers, medan IE4 använder document.all. IE4 använder inte document.getElementById, medan IE5 gör det. NN6 använder förstås inte document.all, men väl document.getElementById. Det betyder att du kan grovt avgöra vilken läsare en besökare har på följande sätt:

var isNN4 = (document.layers) ? true : false;

var isIE4 = (document.all && !document.getElementById) ? true : false;

var isIE5 = (document.all && document.getElementById) ? true : false;

var isNN6 = (!document.all && document.getElementById) ? true : false;

(8)

? 2002 Ulf Tornert

8

Du väljer nu själv hur mycket du vill anpassa dina sidor till olika webbläsare och versioner.

Fortsättningen på det här häftet kommer att vara kompatibla med fjärde generationen, men det bör vara fullt möjligt för dig att skriva om exemplen så att de passar både NN6 och IE5+.

Några enkla exempel

Det här häftet gör sig nog bäst genom att jag kommenterar och visar några enkla exempel tillsammans med sidkoden. Alla exempel finns tillgängliga på adressen

http://www.gy.varmdo.se/~ulftt. Under nästa avdelning kommer du att få möjlighet att träna dina färdigheter genom att lösa övningsuppgifter självständigt. Eftersom allting här egentligen handlar om stilmallar och JavaScript, kan det vara en god idé att ha dessa häften tillgängliga under det här momentet.

Hit me! (ett skolexempel)

I det här exemplet kommer du att få chansen att vinna $10.000. Villkoret är att du lyckas klicka på bilden i sidan. Poängen är ju förstås att så fort du kommer nära bilden så flyttar den på sig.

Hur gör man det här? Ja, det finns säkert flera olika sätt och här är ett av dem.

Koden till sidan är följande

<!-- Netscape accepterar inte inline-stilmallar till lager! Om man lägger den lilla mallen nedan inline i <div>-taggen, kommer det att resultera i att layers[]-vektorn bilr tom !!! Genom att skapa ett regelrätt id genom

#-tecknet kommer NN att acceptera referensen

document.layers["lager"].visibility. Det tog en bra stund att klura ut!

Observera att NN tar inline till ex. <P>. -->

<html>

<head>

<title>hitme.htm</title>

<style type="text/css"><!--

#lager {

position: absolute;

(9)

? 2002 Ulf Tornert

9 visibility: visible;

top: 0px;

left: 0px;

background-color: lightgreen;

}

--></style>

<script language="JavaScript"><!--

// Nedan funkar i NN men inte IE. Om du använder dessa // - ta då bort onMouseOver i bildlänken.

//window.captureEvents(Event.MOUSEOVER);

//window.onmouseover = moveLayer;

var pos = new Array(); // skapa en vektor for (i = 0; i < 4; i++)

pos[i] = i * 100; // Vektorn innehåller talen 0, 100, 200 och 300

var prevX = 0; // globala kontrollvariabler så att inte var prevY = 0; // lagret flyttas till samma plats som innan

function moveLayer() {

var newY = Math.round(Math.random()*3); // ett tal mellan 0 och 3 var newX = Math.round(Math.random()*3); // ett tal mellan 0 och 3 while ((newX == prevX) && (newY == prevY)) // om båda är lika gör om {

newY = Math.round(Math.random()*3);

newX = Math.round(Math.random()*3);

}

if (navigator.appName == 'Netscape') {

document.layers["lager"].top = pos[newY]; // ny y-position document.layers["lager"].left = pos[newX]; // ny x-position prevX = newX;

prevY = newY;

} else {

document.getElementById("lager").style.pixelTop = pos[newY];

document.getElementById("lager").style.pixelLeft = pos[newX];

prevX = newX;

prevY = newY;

} }

function doNothing(){} // dummy-funktion till bildlänken //--></script>

</head>

<body bgcolor="#FFFFFF" text="#000000">

<h1 style="position:absolute; color:#000066; font-family:Garamond;

left:120px">Försök att klicka i bilden!</h1>

<div id="lager">

<a href="javascript:doNothing()" onMouseOver="moveLayer()"

onClick="alert('You win!')" ><img width="100" height="100" border="0"

src="hitme.gif"></a>

</div>

</body>

</html>

(10)

? 2002 Ulf Tornert

10

Till att börja med skapar jag ett id i <style>-taggen som jag sedan skall använda till det lager som innehåller bilden. Jag namnger lagret lager för enkelhets skull. I mallen till detta anger jag att positionen skall vara absolut vid koordinaterna (0, 0) räknat från sidans över vänstra hörn och lagret skall vara synligt med ljusgrön bakgrund. Ökat x-värde medför förflyttning åt höger och ökat y-värde förflyttning neråt. Själva lagret skapar jag med <div>-taggen och anger då

id=”lager”. Notera att detta förfarande är avgörande för att det hela skall fungera i NN (till IE räcker det att skapa mallen inline och referera som vanligt). Här placerar jag en bildlänk och jag utnyttjar händelsen onMouseOver (tillhörande länken) för att anropa funktionen moveLayer(). Varje gång jag rullar muspekaren över bilden kommer därför lagret att förflyttas tillräckligt för att det inte skall gå att klicka i bilden.

Nu skapar jag vektorn pos i <script>-taggen, där jag lagrar värdena 0, 100, 200 och 300. Dessa kommer att utgöra basen för de nya koordinater som lagret slumpvis kommer att få. Jag skapar också två kontrollvariabler prevX och prevY, vilka används till att kontrollera så att lagret vid förflyttning inte erhåller exakt samma position som tidigare – då kan man ju tänka sig att det är ganska lätt att klicka i bilden och vinna. Därefter skapar jag den funktion som utför själva förflyttningen – moveLayer(). Det första som händer där är att två nya variabler skapas – newY och newX – dessa kommer att innehålla lagrets nya koordinater. Variablerna ges varsitt

slumpvärde mellan 0 och 3, sedan använder jag vektorn pos för att tilldela lagret nya positioner.

Kom ihåg att vektorn pos innehåller värdena 0, 100, 200 och 300.

Nu kommer själva funktionaliteten. Jag a nvänder mig av en while-slinga för ändamålet. I villkoret utförs två komparativa tester och ett logiskt. Hela testet kan sammanfattas med ”om newX är lika med prevX OCH om newY är lika med prevY så…” I det fall att de nya koordinaterna är identiska med de gamla kommer nya slumpvärden att tas fram. Därefter utförs själva

förflyttningen. Lägg märke till att jag testar om browsern är Netscape, om det inte stämmer utgår jag från att det är frågan om Explorer (däremot görs inga som helst kontroller över

versionsnummer). Det visar sig nämligen att inte bara lagerreferenser ser olika ut i NN och IE, egenskaperna top och left i NN heter pixelTop och pixelLeft i IE. Akta dig för gråa hår!

Slutligen tilldelar jag variablerna prevX och prevY de nya koordinaterna och förloppet är redo att upprepas i det oändliga.

Ett par frågor för den intresserade: Hur många positioner kan det ”hoppande” lagret anta? Hur kan man tänka sig att utöka dessa? Finns det andra sätt att positionera lagret?

Dolda inforutor i sidan

I webbläsarens statusrad visas en del information under det att du surfar runt på olika sidor. Om du aldrig förut tänkt på det kan du hålla ögonen på statusfältet samtidigt som du rullar med muspekaren över en länk. Där visas då den aktuella adressen som länken refererar till. Men om man vill vara mycket mer tydlig och ”slänga information i ansiktet” på en besökare kan man använda små dolda inforutor i sidan som plötsligt visas när man exempelvis rullar över en länk.

Ett bra exempel på det finns i boken ”Att göra en hemsida” på sidan 132. Jag gör ett liknande här och kommenterar funktionerna. Samtidigt skall vi lära oss hur man gör automatiserade referenser till lager så att samma kod kan användas till både NN och IE. Så här kan det se ut

(11)

? 2002 Ulf Tornert

11

Koden som genererar sidan är

<html>

<head>

<title>inforutor.htm</title>

<style type="text/css">

#text_1 {

position:absolute;

top: 50;

left: 20;

width: 200;

height: 100;

color: #000066;

font-family: Garamond, serif;

font-size: 12pt;

visibility: hidden;

}

#text_2 {

position: absolute;

top: 50;

left: 20;

width: 200;

height: 100;

color: #999999;

font-family: Garamond, serif;

font-size: 12pt;

visibility: hidden;

}

</style>

<script language="JavaScript">

var isNN, isIE;

var lagerRef, styleRef;

if (navigator.appName == 'Netscape') {

isNN = true;

// automatiserar referenser till NN lagerRef = "document.layers";

styleRef = "";

} else

(12)

? 2002 Ulf Tornert

12 {

isIE = true;

// automatiserar referenser till IE lagerRef = "document.all";

styleRef = ".style";

}

function show(lager) {

eval(lagerRef + '["' + lager + '"]' + styleRef +

".visibility='visible'");

// I nyare versioner skriver du:

// eval("document.getElementById('" + lager + "')" + // ".style.visibility = 'visible'");

// och motsvarande nedan...

}

function hide(lager) {

eval(lagerRef + '["' + lager + '"]' + styleRef +

".visibility='hidden'");

}

</script>

</head>

<body bgcolor="#FFFFFF" text="#000000">

<a href="javascript:doNothing()" onMouseOver="show('text_1')"

onMouseOut="hide('text_1')">Länk 1</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

<a href="javascript:doNothing()" onMouseOver="show('text_2')"

onMouseOut="hide('text_2')">Länk 2</a><br>

<div id="text_1">Lite text som förklarar vad Länk 1 leder till...</div>

<div id="text_2">Lite text som förklarar vad Länk 2 leder till...</div>

</body>

</html>

För det första skapar jag två olika id:n, jag kallar dem text_1 och text_2. Dessa används till att skapa de lager som innehåller infotexten som visas när musen svävar över Länk 1 respektive Länk 2. Du bör nu vara ganska familjär med vad mallarna åstadkommer. Lägg märke till att egenskapen visibility har värdet hidden till att börja med – dvs när sidan laddas syns lagren inte! Själva lagren skapas som vanligt med <div>-taggen och jag anger då förstås id=”text_1”

till det lager som innehåller infotext till Länk 1.

I de två länkarna använder jag händelsen onMouseOver för att anropa funktionen show() som visar ett lager. Jag använder sedan onMouseOut för att anropa funktionen hide() som döljer lagret igen. Vilket lager som det är frågan om skickar jag med som argument i anropet. Lägg då märke till att argumentet är en sträng, eftersom jag omsluter värdet med enkla citationstecken (kom ihåg att nästla enkla och dubbla citationstecken korrekt).

Så till själva skriptet. För det första skapar jag två enkla variabler isNN och isIE. Dessa ger jag booleska värden (true eller false) beroende på vilken webbläsare som är aktuell (inte heller här gör jag någon versionskontroll och egentligen använder skriptet endast variabeln isNN…) Därefter skapar jag två variabler som används till att automatisera referenserna till lagren –

(13)

? 2002 Ulf Tornert

13

lagerRef och styleRef. Referensen till egenskapen visibility i lagret text_1 ser ut så här i NN respektive IE:

document.layers[”text_1”].visibility = ”visible”; // NN document.all[”text_1”].style.visibility = ”visible”; // IE

Dvs det finns mycket som är gemensamt för de båda. Om webbläsaren är NN så tilldelar jag

lagerRef strängen ”document.layers” och styleRef den tomma strängen ””. I anropet kommer jag att skicka med namnet på det lager som är aktuellt och detta kommer att vara tillgängligt via variabeln lager. För att skapa en korrekt referens i NN kan jag alltså skriva

lagerRef + '["' + lager + '"]' + styleRef + ".visibility='visible'"

Om jag anropat funktionen med värdet ’text_1’ kommer ovan att vara identiskt med

document.layers[”text_1”].visibility = ’visible’

Var noga med att du känner till hur man konkatenerar strängar och variabler i JavaScript. Men – det räcker tyvärr inte att bara skriva så här. Effekten av det uteblir om vi inte använder den inbyggda funktionen eval(). Denna utför det som står i dess argument och kommer väl till pass lite då och då. Lägg också märke till att variabeln styleRef i det här läget bara är en tom sträng och bidrar därför inte till referensen med någonting alls!

Om webbläsaren istället är IE tilldelar jag variabeln lagerRef värdet document.all och

styleRef värdet .style. En korrekt referens är då exakt detsamma som ovan – vilket förstås är hela vitsen

lagerRef + '["' + lager + '"]' + styleRef + ".visibility='visible'"

Detta kommer nu att vara detsamma som

document.all[”text_1”].style.visibility = ’visible’;

På det här sättet kan jag alltså använda samma funktion till båda lagren och till båda webbläsarna – mycket smidigt! Men en varning: Så här enkelt kommer du inte undan om du nästlar lager. Det kan därför vara bra att tänka efter rejält innan man börjar att skapa en dynamisk sida.

För de nyare versionerna av webbläsare, dvs NN6 och IE5 och framåt rekommenderar jag att använda metoden getElementById() istället. Då ser uttrycket ut så här:

eval("document.getElementById('" + lager + "')" + ".style.visibility = 'visible'");

Pop-up menyer

Många är de sajter som har pop-up menyer av den typ som finns på Start knappen i Windows.

När man rullar över en länk dyker det plötsligt upp en meny där man kan välja ytterligare fler länkar. Detta är förstås en uppenbar fördel – man spar utrymme på sidan. Nackdelen är att de skript som genererar effekten är mycket svåra att få enhetliga och kompatibla med många olika webbläsare och versioner. Om man dessutom har ett avancerat menysystem på sin sajt blir skripten ganska stora och svåra att hantera. Som tur är finns det webbguru’s som gör sådant jobb gratis och sedan delar med sig till oss. Om du behöver coola menyer kan du knycka sådana på exempelvis http://www.dhtmlcentral.com eller http://www.dynamicdrive.com. Men bara

(14)

? 2002 Ulf Tornert

14

för att ni ska få möjlighet att förstå hur en pop-up meny kan vara uppbyggd, tar jag här upp ett mycket enkelt exempel. Med lite påhittighet och stort mått av tålamod går denna metod faktiskt att bygga ut till ett fungerande menysystem. Själva sidan ser ut så här:

När man rullar över bildlänken Meny kommer en liten pop-up meny innehållande fem länkar att dyka upp. Så länge musen står över bilden eller någon av länkarna kommer menyn att vara synlig, men så fort man rullar ut musen utanför dessa kommer menyn att släckas igen. Hur gör man nu detta? Här kommer koden till sidan:

<html>

<head>

<title>popupmenu.htm</title>

<style type="text/css"><!--

/* a:hover fungerar bara i IE */

a:hover{color:#C0C0C0}

a:link{color:#999999}

a:visited{color:#C0C0C0}

#lankar {

background-color: #000066;

font-family: Verdana;

font-size: 12pt;

font-weight: bold;

position:absolute;

top: 30px;

left: 0px;

visibility: hidden;

z-index: 10;

}

#meny {

/* Lägg märke till att IE placerar menybilden vid position (0,0) - NN gör det inte! */

position: absolute;

top: 0px;

left: 0px;

}

--></style>

<script language="JavaScript"><!--

(15)

? 2002 Ulf Tornert

15 var isNN = false;

var layerRef, styleRef;

var t; // global timeout-referens if (navigator.appName == 'Netscape')

{

// mycket slarvig browserkontroll...

isNN = true;

layerRef = "document.layers";

styleRef = "";

} else

{

layerRef = "document.all";

styleRef = ".style";

}

function showMenu() {

eval(layerRef + '["lankar"]' + styleRef + ".visibility='visible'");

/*

I nyare versioner av NN och IE skriver du

document.getElementById('lankar').style.visibility = 'visible';

och motsvarande för att släcka menyn (nedan)

*/}

function slack() {

t = setTimeout("hideMenu()", 1000);

}

function hideMenu() {

eval(layerRef + '["lankar"]' + styleRef + ".visibility='hidden'");

}

function doNothing(){}

//--></script>

</head>

<body bgcolor="#666666" text="#000000">

<a onMouseOver="clearTimeout(t); showMenu()" onMouseOut="slack()"

href="javascript:doNothing()">

<img id="meny" border="0" src="meny.gif"></a>

<div id="lankar">

<a href="hitme.htm" onMouseOver="clearTimeout(t)"

onMouseOut="slack()">Länk nr 1</a><br>

<a href="javascript:doNothing()" onMouseOver="clearTimeout(t)"

onMouseOut="slack()">Länk nr 2</a><br>

<a href="javascript:doNothing()" onMouseOver="clearTimeout(t)"

onMouseOut="slack()">Länk nr 3</a><br>

<a href="javascript:doNothing()" onMouseOver="clearTimeout(t)"

onMouseOut="slack()">Länk nr 4</a><br>

<a href="javascript:doNothing()" onMouseOver="clearTimeout(t)"

onMouseOut="slack()">Länk nr 5</a><br>

</div>

</body>

</html>

Mallen för pseudo-klasserna till länkar har du sett i häftet om stilmallar, om du är osäker kan du gå tillbaka dit. Jag använder som vanligt ett id som jag kallar lankar, med detta skapar jag sedan lagret innehållande mina fem länkar. Det som är viktigt här är att lagret från start är osynligt,

(16)

? 2002 Ulf Tornert

16

eftersom egenskapen visibility har värdet hidden. Dessutom lägger jag till z-index:10 ifall menyn skulle överlappa eventuell text i dokumentet, då garanterar detta att menyn lägger sig ovanpå.

Därefter skapar jag ett id för själva bildlänken – meny. Detta har till enda uppgift att placera bildlänken vid positionen (0,0). Det skulle jag kunna göra med en inline-mall också, men NN har lite problem med dessa så jag tog inga chanser (prova gärna). Lägg märke till att IE faktiskt placerar bilden vid korrekt position medan NN inte gör det.

Själva lagret skapar jag som vanligt. Men nu till det intressanta – skriptet. Eftersom bilden är en länk utnyttjar jag länkens onMouseOver för att anropa de två funktionerna clearTimeout(t)

och showMenu(), vilken endast sätter synligheten till lankar genom visibility=’visible’. (Anledningen till att jag först exekverar clearTimeout(t) är att menyn inte skall släckas om någon rullar från menyn och över bilden igen. Lägg märke till att det går att stapla funktionsanrop till exempelvis onMouseOver.) Men om besökaren nu skulle bestämma sig för att rulla

muspekaren någon annanstans i sidan måste jag ju dölja menyn igen, så jag använder

onMouseOut för att anropa funktionen slack() – det är här finnessen kommer. Jag kan inte dölja lagret direkt vid onMouseOut från bildlänken, om jag gör så kommer lagret med länkarna att släckas ut innan jag ens hinner peka på någon av dem. Jag använder därför den globala variabeln t för att skapa en fördröjning på en sekund innan den funktion som faktiskt döljer lagret anropas –

hideMenu(). Det är vad som sker med satsen

t = setTimeout("hideMenu()", 1000);

Det betyder att lagret kommer att förbli synligt en sekund efter det att musen rullat av bildlänken.

Gott om tid att hinna peka på en av de andra länkarna alltså! Då måste jag ju se till först av allt att det funktionsanrop som är på väg avbryts – det är vad som sker med satsen

clearTimeout(t)

vilken anropas via samtliga länkars onMouseOver. Varje länk har sedan samma funktionsanrop vid onMouseOut som bildlänken förstås. Det innebär att lagret släcks ut efter en sekund oavsett varifrån muspekaren lämnar lagret! När man väl kommer på tricket känns det nästan oförskämt enkelt…

Scrolla lager

På många olika sajter har man använt DHTML för att åstadkomma scrollningfunktioner som förhoppningsvis skall upplevas som lite coola. När jag först stötte på dem tyckte jag faktiskt det också, men sedan kan man ju undra vad man ska ha dem till… Ja, ja, här är dem i alla fall i studiesyfte! Jag har här tillverkat två separata filer – en för NN och en för IE, inte för att det inte går att skapa en gemensam fil för de båda, utan för att illustrera ett bekymmer. Det visade sig att de inline-mallar som jag infogade i bildlänkarna för att placera dessa på sidan, ordnade så att sidan helt slutade fungera i NN! Referenserna till lagren är helt korrekta, men ett par inline-mallar som inte har ett dyft med skript eller referenser att göra slog ut hela scrollningsfunktionen – det tog mig en stund att fundera ut vad som var fel, särskilt som jag inte fick ett enda felmeddelande från Netscape…

I filen scroll_layers.htm finns inline-mallarna med och i filen scroll_layers_NN.htm är de borttagna. Jämför gärna referenserna – de är identiska. Okej, när sidan laddas ser den ut så här

(17)

? 2002 Ulf Tornert

17

Om jag rullar över bilderna kommer texten att scrolla inuti det blå lagret

Här är koden

<html>

<head>

<title>scroll_layers.htm</title>

<style type="text/css"><!--

#theFrame

(18)

? 2002 Ulf Tornert

18 {

position: absolute;

left: 150px;

width: 450px;

height: 350px;

clip: rect(0px 450px 350px 0px);

background-color: #000066;

}

#content {

position: absolute;

background-color: #000066 }

P {

margin-top: 0.5em;

margin-left: 0.5em;

font-family: "Courier New";

font-size: 16pt;

color: white;

}

--></style>

<script language="JavaScript"><!-- var t;

function doNothing(){}

function stop() {

clearTimeout(t);

}

function scrollUp() {

if (navigator.appName == 'Netscape') {

document.theFrame.document.content.top -= 2;

} else {

document.all.content.style.pixelTop -= 2;

// i nyare versioner:

// document.getElementById('content').style.pixelTop -= 2;

// och motsvarande för scrollDown() }

t = setTimeout("scrollUp()", 50);

}

function scrollDown() {

if (navigator.appName == 'Netscape') {

document.theFrame.document.content.top += 2;

} else {

document.all.content.style.pixelTop += 2;

(19)

? 2002 Ulf Tornert

19 }

t = setTimeout("scrollDown()", 50);

}

//--></script>

</head>

<body bgcolor="#FFFFFF" text="#000000">

<div id="theFrame">

<div id="content"><P>

Den här effekten har du alldeles säkert stött på om du surfar mycket. Det finns kanske inga

uppenbara fördelar med den - förutom att allt övrigt på sidan ligger helt stilla och att

det ger sidan någorlunda proffsig känsla. Eller?<br><br>

Zzzzzzzzzzzzz...

</P>

</div></div>

<a style="position:absolute; left: 0; top: 0"

href="javascript:doNothing()" onMouseOver="scrollUp()"

onMouseOut="stop()"><img border="0" src="up.gif"></a>

<a style="position:absolute; left: 0; top: 250"

href="javascript:doNothing()" onMouseOver="scrollDown()"

onMouseOut="stop()"><img border="0" src="down.gif"></a>

</body>

</html>

För det första skapar jag som vanligt ett par olika id:n som syftar till att användas med <div>- taggen. Det som är nytt är egenskapen clip i lagret theFrame, vilket omger det lager som scrollas. Det är nämligen så att jag måste ange hur eventuellt överskjutande innehåll skall

hanteras. Om jag inte anger clip-regionen explicit kommer browsern att utöka lagret så att saker och ting får plats – men det vill jag ju inte. Egenskapen clip anges så här

clip: rect(0px 450px 350px 0px);

där värdenena innanför parentesen avser top, right, bottom och left (i den ordningen). Satsen ovan kommer alltså att sätta det aktuella lagrets clip-region till lagrets yttre kanter. Dvs så fort något kommer utanför lagrets bredd och höjd syns det inte, utan döljs under dess kanter.

Jag sätter egenskapen clip till det lager jag kallar theFrame – det lager som fungerar som fönster för det lager som innehåller texten. Lagret med text kallar jag content och har en mycket enkel mall. Sedan skapar jag lagren och i det här fallet är det viktigt att de är nästlade. Detta eftersom innehållet i content skall påverkas av clip i lagret theFrame.

Jag placerar även ut de båda bildlänkarna och använder länkarnas onMouseOver och

onMouseOut för att anropa funktionerna scrollDown(), scrollUp() och stop(). Som href till länkarna använder jag en dummy-funktion. Nu börjar du säkert se ett mönster i hur jag tillverkar dynamiska sidor!

Nu till funktionerna. När onMouseOver i en länk anropar exempelvis scrollDown() kommer funktionen att köras igenom en gång. Men eftersom jag vill att scrollningen skall fortsätta under hela den tid musen svävar över bilden måste jag hitta på ett sätt att anropa funktionen igen. Det löser man enkelt med det som kallas rekursiva funktionsanrop – jag låter helt enkelt

scrollDown() ropa på sig själv. Men risken är då att jag skapar en oändlig slinga som inte går att

(20)

? 2002 Ulf Tornert

20

bryta! Jag vill ju att scrollningen skall upphöra då jag rullar av musen från bilden. Hur gör jag detta? Svaret ligger i att skapa en global variabel med vilken jag använder den inbyggda

funktionen setTimeout(). Med denna kan jag dels sätta lämpligt tidsintervall mellan anropen – dvs säga bestämma hur fort scrollningen går, dels kan jag från en annan funktion avbryta ett sådant anrop via clearTimeout(). Om jag med onMouseOut anropar clearTimeout() och refererar till den aktuella timeout:en avbryts scrollningen. Precis som jag vill ha det! Jag sätter en timeout inuti exempelvis scrollDown() med satsen

t = setTimeout("scrollDown()", 50);

Det innebär att scrollDown() kommer att exekveras efter 50 ms och variabeln t refererar till den aktuella timeout:en. Om jag via onMouseOut i funktionen stop() exekverar satsen

clearTimeout(t);

kommer anropet till scrollDown() inte att utföras och scrollningen avbryts. Exakt detsamma gäller för funktionen scrollUp().

Animera lager

Bara för att demonstrera tekniken med hur man kan animera lager i en sida skall jag ta ett enkelt exempel. Koden är inte särskilt genomtänkt och kan automatiseras mycket mer, men det är inte poängen just nu. När man laddar sidan kommer små ankungar att ”segla” ner över sidan. När de passerat den undre kanten kommer de snart tillbaka vid sidans övre kant, likaså om de råkar hamna lite för långt åt sidan. I ett visst ögonblick såg sidan ut så här

Exemplet heter animera_lager.htm, titta på kursens hemsida så får du se den ”live”. Koden som genererar sidan är

<html>

<head>

(21)

? 2002 Ulf Tornert

21

<title>animera_lager.htm</title>

<style type="text/css"><!--

#lager1 {position:absolute; visibility:visible; top:0; left:100;}

#lager2 {position:absolute; visibility:visible; top:100; left:200;}

#lager3 {position:absolute; visibility:visible; top:-50; left:450;}

#lager4 {position:absolute; visibility:visible; top:200; left:600;}

#lager5 {position:absolute; visibility:visible; top:20; left:320;}

--></style>

<script language="JavaScript"><!--

function animate() {

var nr, deltaX, deltaY;

nr = Math.round(Math.random()*4 + 1); // heltal mellan 1 och 5 deltaX = Math.round(Math.random()*20);

deltaX -= 10; // heltal mellan -10 och 10 deltaY = Math.round(Math.random()*10);

if (document.layers) // annat sätt att testa om NN {

if (document.layers[nr - 1].top >= 800

|| document.layers[nr - 1].left >= 610 || document.layers[nr - 1].left <= -10) {

document.layers[nr - 1].top = 0; // återför till toppen av sidan document.layers[nr - 1].left = Math.round(Math.random()*800);

}

document.layers[nr - 1].left += deltaX;

document.layers[nr - 1].top += deltaY;

}

else // utgår från att det är IE {

var vanster = eval("document.getElementById('lager" + nr +

"').style.pixelLeft");

var toppen = eval("document.getElementById('lager" + nr +

"').style.pixelTop");

if (toppen >= 800 || vanster >= 610 || vanster <= -10) {

eval("document.getElementById('lager" + nr + "').style.pixelTop = 0");

eval("document.getElementById('lager" + nr + "').style.pixelLeft = Math.round(Math.random()*800)");

}

eval('document.getElementById("lager' + nr + '")' +

".style.pixelLeft += deltaX");

eval('document.getElementById("lager' + nr + '")' + ".style.pixelTop += deltaY");

} setTimeout("animate()", 20);

}

//--></script>

</head>

<body bgcolor="#FFFFFF" text="#000000" onLoad="animate()">

<div id="lager1"><img src="duck.gif"></div>

<div id="lager2"><img src="duck.gif"></div>

<div id="lager3"><img src="duck.gif"></div>

<div id="lager4"><img src="duck.gif"></div>

<div id="lager5"><img src="duck.gif"></div>

(22)

? 2002 Ulf Tornert

22

<font face="Verdana, Arial, Helvetica, sans-serif" size="2">Det var en gång i tiden en ful ankunge...</font>

</body>

</html>

Hela sidans funktion är egentligen ganska enkel. Jag skapar 5 unika id, med vilka jag skapar 5 lager innehållande bilden duck.gif. Lagren får namnen lager1, lager2 och så vidare. Jag använder händelsen onLoad för att anropa animate() första gången, därefter låter jag funktionen anropa sig själv med 20 millisekunders fördröjning. Inuti funktionen skapar jag tre variabler – nr för att hålla referensen till det aktuella lagret, deltaX och deltaY för att hålla information om storleken på nästa förflyttning. Därefter använder jag Math-objektet för att skapa ett heltal mellan 1 och 5, med satsen

nr = Math.round(Math.random()*4 + 1);

Det blir så eftersom funktionen random() lämnar ett decimaltal mellan 0 och 1 (multiplicera sedan detta med 4 och lägg till 1). Lägg nu märke till att en korrekt referens till något av lagren i NN är

document.layers[nr - 1]

eftersom det första lagret har referensen document.layers[0]. En korrekt referens till samma lager i IE kan se ut så här

'document.getElementById(”lager' + nr + '")'

Dock måste man här använda funktionen eval() för att aktivera önskad förändring på lagret (se i koden).

Sedan tilldelar jag värden på deltaX och deltaY. Nu kommer själva idén bakom exemplet: Jag testar om browsern är NN med satsen

if (document.layers)

och därefter testar jag direkt om lagret har överskridit de yttre gränserna top:800 eller left:-10 eller left:610. Om så är fallet återför jag lagret till övre kanten av sidan (vid en slumpmässig x- position). Om villkoret inte är uppfyllt håller sig alltså lagret innanför givna yttre gränser och jag ökar därför på egenskaperna top och left med värdena på deltaY resp. deltaX.

Om browsern inte är NN utgår jag lite slarvigt från att det är frågan om IE och gör samma sak som ovan fast för IE:s speciella referenser. Lägg märke till att jag använder funktionen eval() för att ”aktivera” aktuella förändringar.

Slutligen anropar jag animate() med en fördröjning på 20 ms via satsen

setTimeout("animate()", 20);

Detta kallas då ett rekursivt funktionsanrop (se häftet om JavaScript).

(23)

? 2002 Ulf Tornert

23

Övningsuppgifter

Den här delen är tänkt att ge dig träning i att lösa uppgifter på egen hand. Om du verkligen kör fast så pass att du inte kommer vidare kan du ladda ner övningsuppgifterna i sin helhet på kursens hemsida http://www.gy.varmdo.se/~ulftt. För att ladda filen ovnuppg_1.htm kan du skriva in adressen http://www.gy.varmdo.se/~ulftt/dhtml/ovnuppg_1.htm, eller följa länkarna. Gör dig själv en tjänst – tjuvtitta inte innan du verkligen har försökt! Risken är annars stor att du inte lär dig så mycket.

1. ovnuppg_1.htm

Skapa ett lager med <div>-taggen som vanligt. Använd inga inline-mallar, eftersom vi strävar efter att få sidan att fungera även i NN4. Låt detta lager ha mörkblå bakgrund och vit text, samt bredden 200px och höjden 100px. Placera lagret vid koordinaterna (50, 100). Skapa en länk i sidan vilken döljer lagret när man klickar på den.

2. ovnuppg_2.htm

Ändra nu på funktionen som döljer lagret i uppgiften ovan. Istället för att bara göra lagret osynligt skall du animera en förflyttning åt vänster. När lagret hamnat utanför bild skall förflyttningen avbrytas. Tips: Utnyttja rekursiva funktionsanrop med setTimeout(). Använd också en global variabel som representerar positionen i x-led för lagret. Värdet på denna variabel ändras varje gång funktionen anropas och därefter testar man med en if-sats om förloppet skall upprepas.

3. ovnuppg_3.htm

Skapa en sida som använder ett – vad jag kallar – skakande lager för att få en textrad att stå ut mer i en sida. Försök skapa en sida som fungerar i både NN4, IE4, IE5+ och NN6. Om du får problem (vilket inte är osannolikt) kan du göra separata sidor för de olika versionerna. Du måste kunna förflytta ett lager (fram och tillbaka, med kanske 1 – 5 pixlar) och du måste kunna hantera rekursiva funktionsanrop med setTimeout() för att skakningen skall uppfattas av ögat.

4. ovnuppg_4.htm

Gör en liten fotovisning där du lägger tre bilder i var sitt lager. Se till att bilderna är precis lika stora och att endast en av dem är synlig. Du kan gärna använda de bilder som jag använder – efter adressen http://www.gy.varmdo.se/~ulftt/dhtml/ skall du i så fall lägga till filnamnen hands.jpg, dune.jpg och audio.jpg. Placera varje lager på exakt samma position. Gör gärna en slags stegningsfunktion, där du visar två länkar – bakåt och framåt. När man klickar i dessa skall nästa bild i sekvensen visas. Tänk dock på att sekvensen bör starta om från början med den första bilden om den sista bilden visas och besökaren trycker på framåt. Det omvända bör ju gälla om den första bilden i sekvensen visas och någon klickar bakåt. Om du tycker detta är krångligt kan du även lägga ut tre länkar, en för varje bild, som sköter visningen. Så här kan det se ut när sidan laddas

(24)

? 2002 Ulf Tornert

24

5. ovnuppg_5.htm

Skapa en sida som visar en svensk flagga på varje svensk flaggdag. Om det inte är flaggdag skall ingenting visas. Flaggdagar hittar du i almanackan och du får föra in dessa i en vektor (en ”array”), mot vilken du undersöker eventuell överensstämmelse med ”dagens” aktuella datum. För att lösa detta måste du hantera objektet Date som står beskrivet i JavaScript- häftet. (Kom ihåg: Om du verkligen kör fast, så finns koden på kurssidan). Så här kan det ut:

Nu är det här momentet slut. Exemplen här kan förstås kännas ganska tama, men jag hoppas ändå att de har gett någonting. Jag har full förståelse för om någon tycker att det här är svårt. Det är inte lätt – särskilt eftersom webbläsare fungerar ganska olika. Webbdesigner åldras säkert dubbelt så snabbt som oss andra, fast å andra sidan har de säkert en hel del kul också...

Lycka till med dina dynamiska sidor i framtiden!

Ulf Tornert

Figur

Updating...

Referenser

Updating...

Relaterade ämnen :