Avatar billede flodhesten Nybegynder
17. juli 2012 - 21:06 Der er 23 kommentarer

Forkorte kode?

Hej, jeg er helt ny til javascript.

Jeg har en kode med et helt specifikt mønster, men jeg kan ikke lure hvordan jeg kan gøre den kortere/pænere. Kan du måske hjælpe lidt?

var color1 = document.getElementById('color1');
var active1 = false;
color1.style.cursor = 'pointer';
color1.onclick = function() {
    if (!active1) {
        color1.style.border = '2px solid #333';
        active1 = true;
    } else {
        color1.style.border = '0px solid black';
        active1 = false;
    }
};
var color2 = document.getElementById('color2');
var active2 = false;
color2.style.cursor = 'pointer';
color2.onclick = function() {
    if (!active2) {
        color2.style.border = '2px solid #333';
        active2 = true;
    } else {
        color2.style.border = '0px solid black';
        active2 = false;
    }
};
var color3 = document.getElementById('color3');
var active3 = false;
color3.style.cursor = 'pointer';
color3.onclick = function() {
    if (!active3) {
        color3.style.border = '2px solid #333';
        active3 = true;
    } else {
        color3.style.border = '0px solid black';
        active3 = false;
    }
};


På forhånd tak.
Avatar billede olebole Juniormester
18. juli 2012 - 00:38 #1
<ole>

Der kan være flere tilgange - bedre - mere overskuelige - men ikke nødvendigvis væsentligt kortere  =)

Event handlers bør ikke 'sættes' på et element, men 'tilføjes'. Når du gør som i koden ovenfor, overskriver du evt. andre handlers på elementet.

"Jamen, der skal kun være den ene handler, Ole". Ja, ligenu, men du ved ikke, hvad du eller en anden vil udbygge koden med om et halvt år.

Derfor bruger man metoden ELEMENT.addEventListener, men IE kræver stadig sin egen ELEMENT.attachEvent. Du kan i stedet bruge følgende konstruktion, hvor variablen setEvent sættes til den understøttede handler setter, når siden loader:

var setEvent = (function(){
    if (window.addEventListener) return function(elm, sType, fn) {
        elm.addEventListener(sType, fn, false);
    };
    return function(elm, sType, fn) {
        elm.attachEvent("on"+sType, fn);
    };
})();

Herefter har følgende to forslag til en anden måde at gøre tingene på. Først en procedural, som minder mest om den måde, du koder på nu:

function colorClick(e) {
    var elmSrc = e.target||event.srcElement;
    if (elmSrc.getAttribute("act")==="true") {
        color.style.border = 0;
        elmSrc.setAttribute("act", "false");
    } else {
        color.style.border = "2px solid #333";
        elmSrc.setAttribute("act", "true");
    }
}
function makeClickable(sId) {
    var color = document.getElementById(sId);
    color.style.cursor = "pointer";
    setEvent(color, "click", colorClick);
}

for (var i=1; i<4; i++) makeClickable("color"+i);

- og en mere object orienteret:

function ClickableElement(sId) {
    var me = this;
    this.elm = document.getElementById(sId);
    this.elm.style.cursor = "pointer";
    this.active = false;
    setEvent(this.elm, "click", function(){me.toggle()});
}
ClickableElement.prototype.toggle = function(){
    this.elm.style.border = this.active ? 0 : "2px solid #333";
    this.active = !this.active;
}

for (var i=1; i<4; i++) new ClickableElement("color"+i);


/mvh
</bole>
Avatar billede flodhesten Nybegynder
18. juli 2012 - 13:04 #2
Det er meget fornemt.

Tak takker for lektionen og anmoder om et svar :-)
Avatar billede flodhesten Nybegynder
18. juli 2012 - 15:49 #3
Jeg kom til at tænke hvad man kan gøre, hvis man nu vil have nogle af de her farve-div (som du sikkert har regnet ud jeg har) til at være aktive fra start?

Hmm...
Avatar billede olebole Juniormester
18. juli 2012 - 16:28 #4
Du kunne gøre sådan ved instantieringen af objekterne, hvis du bruger OO-versionen:

var aClickables = {};

for (var i=1; i<4; i++) aClickables["color"+i] = new ClickableElement("color"+i);

aClickables.color2.toggle();
Avatar billede olebole Juniormester
18. juli 2012 - 16:29 #5
Ups ... forkert BB-kode  :D

var aClickables = {};

for (var i=1; i<4; i++) aClickables["color"+i] = new ClickableElement("color"+i);

aClickables.color2.toggle();
Avatar billede olebole Juniormester
18. juli 2012 - 16:31 #6
- og jeg samler ikke points. Du lægger bare selv et svar og accepterer det, så tråden lukkes. Men tak for tilbudet  =)
Avatar billede flodhesten Nybegynder
18. juli 2012 - 16:43 #7
Når jeg prøver at implementere den, er jeg så heldig at få en fejl:

[code]caught TypeError: Cannot read property 'value' of undefined [/code]

Er der noget der ikke er initialiseret eller hvordan skal det tolkes?
Avatar billede flodhesten Nybegynder
18. juli 2012 - 16:47 #8
Argh, det må være fordi jeg har forsøgt at modificere den med min egen slamkode...

ClickableElement.prototype.toggle = function(){
    var color = this.elm.getAttribute("name");
    if (!this.active) {
        document.search.colors.value += color+",";
    } else {   
        var n = document.search.colors.value.indexOf(color+",");
        if (n >= 0) {
            document.search.colors.value = document.search.colors.value.replace(color+",","");
        }       
    }
    this.elm.style.border = this.active ? 0 : "2px solid #333";
    this.active = !this.active;
}
Avatar billede olebole Juniormester
18. juli 2012 - 16:53 #9
Hvad skal der helt præcist ske?
Avatar billede flodhesten Nybegynder
18. juli 2012 - 17:27 #10
Jeg ønsker at have et hidden input-felt med navne på valgte farver. Hver div har en navne-attribut såsom "RED", "BLACK". Dette skjulte input felt skal så opdateres når man til- eller fravælger farver.

Ideen med det skjulte input felt er at jeg vil have sendt de valgte farver med over GET.

Giver det mening? :)
Avatar billede olebole Juniormester
18. juli 2012 - 17:56 #11
Så kunne en samlet løsning se sådan ud:

<script type="text/javascript">
var aClickables = {},
oColors = {};
function formSubmitActions(elmForm) {
    var x, a = [];
    for (x in oColors) a.push(oColors[x]);
    elmForm.colors.value = a.join(",");
}
function ClickableElement(sId) {
    var me = this;
    this.elm = document.getElementById(sId);
    this.elm.style.cursor = "pointer";
    this.active = false;
    this.color = this.elm.getAttribute("name");
    setEvent(this.elm, "click", function(){me.toggle()});
}
ClickableElement.prototype.toggle = function(){
    if (this.active) {
        this.elm.style.border = 0;
        oColors[this.color] = 1;
    } else {
        this.elm.style.border = "2px solid #333";
        delete oColors[this.color];
    }
    this.active = !this.active;
}

for (var i=1; i<4; i++) new ClickableElement("color"+i);
aClickables.color2.toggle();
</script>

<form action="" method="post" onsubmit="formSubmitActions(this)">
<input name="colors" type="hidden">
... ... ...
</form>

Så slår du også kun elementets farve op én gang - nemlig ved instantieringen af dets klikbare objekt. De valgte farver holder jeg gemmer jeg i objektet oColors - og jeg smider dem først i formen på dennes onsubmit event
Avatar billede olebole Juniormester
18. juli 2012 - 17:57 #12
Håber, du får mening ud af den lidt vrøvlede forklaring. Sådan kan det gå, når man prøver at rette et indlæg for at gøre det mere klart  *LoL*
Avatar billede olebole Juniormester
18. juli 2012 - 17:59 #13
Ups ... fejl! Sådan skal objektets værdier gemmes:

for (x in oColors) a.push(x);
Avatar billede flodhesten Nybegynder
18. juli 2012 - 18:41 #14
Det ser snedigt ud. Jeg får dog et problem. Den her linje kode kaster en fejl fra sig:

aClickables.color1.toggle();

Fejlen er: Uncaught TypeError: Cannot call method 'toggle' of undefined

Lige for at der ikke skal være tvivl, så min kode således ud nu:

________________________________________________

<script type="text/javascript">

var setEvent = (function(){
    if (window.addEventListener) return function(elm, sType, fn) {
        elm.addEventListener(sType, fn, false);
    };
    return function(elm, sType, fn) {
        elm.attachEvent("on"+sType, fn);
    };
})();

var aClickables = {},
oColors = {};
function formSubmitActions(elmForm) {
    var x, a = [];
    for (x in oColors) a.push(x);
    elmForm.colors.value = a.join(",");
}
function ClickableElement(sId) {
    var me = this;
    this.elm = document.getElementById(sId);
    this.elm.style.cursor = "pointer";
    this.active = false;
    this.color = this.elm.getAttribute("name");
   
    setEvent(this.elm, "click", function(){me.toggle()});
}
ClickableElement.prototype.toggle = function(){
    if (this.active) {
        this.elm.style.border = 0;
        oColors[this.color] = 1;
        alert(oColors);
    } else {
        this.elm.style.border = "2px solid #333";
        delete oColors[this.color];
    }
    this.active = !this.active;
}

for (var i=1; i<4; i++) new ClickableElement("color"+i);

aClickables.color1.toggle();

</script>

<form method="get" action="result.php" name="search" onsubmit="formSubmitActions(this)">

<input type="hidden" name="colors" value="" />
....
<input type="submit" value="Submit" />
</form>

<div id="color1" name="WHITE" style="margin: 5px; width: 20px; height: 20px; background-color: #FFFFFF"></div>
<div id="color2" name="CREAM" style="margin: 5px; width: 20px; height: 20px; background-color: #F2EAC6"></div>
<div id="color3" name="YELLOW" style="margin: 5px; width: 20px; height: 20px; background-color: #F8E275"></div>

_____________________________________

Jeg synes dog heller ikke jeg modtager noget i min querystring. Jeg forventer noget ala ?colors=BLACK,RED....

Hmm, mon jeg gør noget forkert?
Avatar billede olebole Juniormester
18. juli 2012 - 19:21 #15
Der er flere issues. For det første havde jeg goofed noget af koden op - og skrevet den uden at teste noget. Sorry  =)

Du kan i følge standarden ikke bruge en name attribut på et DIV. Brug i stedet en CSS-klasse. Det gør også din HTML-kode slankere.

Når du instantierer dine klikbare objekter (kører for løkken), skal elementerne være skrevet ud til browseren. Da den læser koden ligesom du og jeg, kan du ikke kalde elementer fra et JavaScript, som står skrevet før elementerne. De er så at sige ikke født endnu og kan derfor ikke høre, når du kalder på dem.

Du skal altså enten skrive koden under elementernes HTML-kode, eller - som jeg vil anbefale - kalde en funktion på sidens onload event. Den funktion instantierer så objekterne.

Her er et testet forslag  =)

<style type="text/css">
.colors {
    display: inline-block;
    background: #ccc;
    padding: 10px;
}
.colors div {
    margin: 5px;
    width: 20px;
    height: 20px;
}
.WHITE {
    background: #FFFFFF;
}
.CREAM {
    background: #F2EAC6;
}
.YELLOW {
    background: #F8E275;
}
</style>
<script type="text/javascript">
var setEvent = (function(){
    if (window.addEventListener) return function(elm, sType, fn) {
        elm.addEventListener(sType, fn, false);
    };
    return function(elm, sType, fn) {
        elm.attachEvent("on"+sType, fn);
    };
})();

var aClickables = {},
oColors = {};
function formSubmitActions(elmForm) {
    var x, a = [];
    for (x in oColors) a.push(x);
    elmForm.colors.value = a.join(",");
    alert(elmForm.colors.value);
}
function ClickableElement(sId) {
    var me = this;
    this.elm = document.getElementById(sId);
    this.elm.style.cursor = "pointer";
    this.active = false;
    this.color = this.elm.className;
   
    setEvent(this.elm, "click", function(){me.toggle()});
}
ClickableElement.prototype.toggle = function(){
    if (this.active) {
        this.elm.style.border = 0;
        delete oColors[this.color];
    } else {
        this.elm.style.border = "2px solid #333";
        oColors[this.color] = 1;
    }
    this.active = !this.active;
    alert(JSON.stringify(oColors, null, "  "));
}

function initColors() {
    for (var i=1; i<4; i++) aClickables["color"+i] = new ClickableElement("color"+i);
    aClickables.color1.toggle();
}
setEvent(window, "load", initColors);
</script>

<form method="get" action="result.php" name="search" onsubmit="formSubmitActions(this)">

<input type="hidden" name="colors" value="" />
....
<input type="submit" value="Submit" />
</form>

<div class="colors">
    <div id="color1" class="WHITE"></div>
    <div id="color2" class="CREAM"></div>
    <div id="color3" class="YELLOW"></div>
</div>

Script og stylesheet skriver du så i HEAD elementet. Stylesheets skal stå i HEAD, mens scripts også må stå i BODY. Af vedligeholdelses grunde kan det dog godt betale sig at skrive det hele i HEAD - og meget gerne som importerede filer
Avatar billede olebole Juniormester
18. juli 2012 - 19:25 #16
Læg i øvrigt mærke til, at jeg bruger JSON.stringify til at alerte objektet med. Nice feature til det formål  *o)
Avatar billede olebole Juniormester
18. juli 2012 - 19:41 #17
Derudover kan du importere jQuery i dokumentet og skrive en kode, som er en hel del mindre. Det lærer du bare ikke en dyt JavaScript af.

Når du er blevet rigtig god til JS, kan du med fordel bruge libraries som YUI eller jQuery - for så kan du gennemskue, hvornår og hvordan det er fornuftigt at bruge en library metode, og hvornår det ikke er. Samtidig kan du skrive din egen, alternative kode.

End ikke de mest avancerede snedkermaskiner kan lave et mahognibord i professionel standard til dig, hvis du ikke ved, hvordan man laver et bord. Uanset, hvor god du er til at programmere maskinerne  =)
Avatar billede flodhesten Nybegynder
18. juli 2012 - 22:23 #18
Jeg siger endnu engang tak for lektionen. Det har givet blod på tanden :)

Jeg tror dog aldrig rigtig jeg bliver gode venner med javascripts fejlbeskeder. De fortæller da absolut ingenting om hvor i koden den er gal.

En sidste ting jeg kunne overveje at implementere: så snart man klipper på en farve, så skal den kalde submit(). Ville det være svært at sætte ind i koden?

Ellers kan det også være vi skal til at afslutte denne tråd - du har jo svaret på mit oprindelige spørgsmål for lang tid siden :-)
Avatar billede flodhesten Nybegynder
18. juli 2012 - 22:24 #19
Klikker* på en farve
Avatar billede olebole Juniormester
19. juli 2012 - 18:53 #20
Hvis formen skal submitte, så snart du klikker på en farve, er det vel ikke nødvendigt at indsamle de farver, der klikkes på(?)
Avatar billede flodhesten Nybegynder
19. juli 2012 - 19:02 #21
Tjo, for som jeg spurgte tidligere om, så skal der være nogle af farverne der er markerede fra start. De bliver jo sendt med GET og derfor kan jeg sætte dem som markeret fra start hvis en farveværdi indgår i query-stringen :)
Avatar billede olebole Juniormester
19. juli 2012 - 19:18 #22
Prøv at skrive scriptet om til noget i denne stil. Så diskriminerer du mellem 'interne' kald til click handleren (fra elementerne selv) - og 'eksterne' (når du sætte de default valgte):

<script type="text/javascript">
var setEvent = (function(){
    if (window.addEventListener) return function(elm, sType, fn) {
        elm.addEventListener(sType, fn, false);
    };
    return function(elm, sType, fn) {
        elm.attachEvent("on"+sType, fn);
    };
})();

var aClickables = {},
oColors = {};
function formSubmitActions(elmForm) {
    var x, a = [];
    for (x in oColors) a.push(x);
    elmForm.colors.value = a.join(",");
    alert(elmForm.colors.value);
}
function ClickableElement(sId) {
    var me = this;
    this.elm = document.getElementById(sId);
    this.elm.style.cursor = "pointer";
    this.active = false;
    this.color = this.elm.className;
    if (!this.elmForm) this.elmForm = this.elm.form;
   
    setEvent(this.elm, "click", function(){me.toggle(true)});
}
ClickableElement.prototype.toggle = function(bInternal){
    if (this.active) {
        this.elm.style.border = 0;
        delete oColors[this.color];
    } else {
        this.elm.style.border = "2px solid #333";
        oColors[this.color] = 1;
    }
    this.active = !this.active;
    if (bInternal===true) {
        formSubmitActions(this.elmForm);
        this.elmForm.submit();
    }
    alert(JSON.stringify(oColors, null, "  "));
}

function initColors() {
    for (var i=1; i<4; i++) aClickables["color"+i] = new ClickableElement("color"+i);
    aClickables.color1.toggle();
}
setEvent(window, "load", initColors);
</script>
Avatar billede olebole Juniormester
19. juli 2012 - 19:20 #23
Sorry, nej det duer heller ikke. Det vil kun virke, hvis der var tale om form elementer. Løsningen skal skrives om til noget andet, men jeg er på vej ud af døren, så det bliver i hvertfald ikke nu
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester