Javascript vereenvoudigen repeat

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Brynnie
  • Registratie: Februari 2001
  • Niet online
Hallo allemaal,

In een website die ik moet onderhouden, kom ik volgende code tegen:

JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
                    element1 = document.getElementById("H1");
                    element1.onclick = function () {
                        alert('H1: ' + element1.getAttribute('data-tlt'));
                    };
                    element2 = document.getElementById("H2");
                    element2.onclick = function () {
                        alert('H2: ' + element2.getAttribute('data-tlt'));
                    };
                    element3 = document.getElementById("H3");
                    element3.onclick = function () {
                        alert('H3: ' + element3.getAttribute('data-tlt'));
                    };
                    element4 = document.getElementById("H4");
                    element4.onclick = function () {
                        alert('H4: ' + element4.getAttribute('data-tlt'));
                    };
                    element5 = document.getElementById("H5");
                    element5.onclick = function () {
                        alert('H5: ' + element5.getAttribute('data-tlt'));
                    };
                    element6 = document.getElementById("H6");
                    element6.onclick = function () {
                        alert('H6: ' + element6.getAttribute('data-tlt'));
                    };
                    element7 = document.getElementById("H7");
                    element7.onclick = function () {
                        alert('H7: ' + element7.getAttribute('data-tlt'));
                    };
                    element8 = document.getElementById("H8");
                    element8.onclick = function () {
                        alert('H8: ' + element8.getAttribute('data-tlt'));
                    };
                    element9 = document.getElementById("H9");
                    element9.onclick = function () {
                        alert('H9: ' + element9.getAttribute('data-tlt'));
                    };
                    .....
                    elementX = document.getElementById("HX");
                    elementX.onclick = function () {
                        alert('HX: ' + elementX.getAttribute('data-tlt'));
                    };


Daar gaat mijn haar van recht staan en zegt mijn intuïtie dat er een veel elegantere manier is om dit te doen.

Echter, wanneer ik onderstaande code probeer, krijgt elke alert de waarde van het laatste element..
JavaScript:
1
2
3
4
5
6
                    for (var i = 1; i < 10; i++) {
                        element = document.getElementById("H" + i);
                        element.onclick = function () {
                            alert('H' + i + ': ' + element.getAttribute('data-tlt'));
                        };
                    }


Wat doe ik verkeerd? Dit moet toch te vereenvoudigen zijn?

De HTML met de id's waarnaar H1, H2 en zo verder, verwijzen, wordt clientside gegenereerd door JSON op te halen met een AJAX call. Aan die code kan ik weinig tot niks veranderen.

Acties:
  • 0 Henk 'm!

  • HyperioN
  • Registratie: April 2003
  • Laatst online: 07-10 22:02
JavaScript:
1
2
3
4
5
6
for (var i = 1; i < 10; i++) {
    var element = document.getElementById("H" + i);
    element.onclick = function () {
        alert('H' + i + ': ' + element.getAttribute('data-tlt'));
     };
}

https://toddmotto.com/eve...w-about-javascript-scope/

Acties:
  • 0 Henk 'm!

  • Brynnie
  • Registratie: Februari 2001
  • Niet online
Het enige verschil met mijn oplossing is bij jou het woordje var op lijn 2. Bedoel je dat er door var te zetten, telkens een nieuwe variable wordt aangemaakt voor die scope?

Ik heb je oplossing net geprobeerd en ook daarbij krijg ik bij elke alert enkel het data-tlt van de laatste H (H9 in bovenstaand geval).

Acties:
  • 0 Henk 'm!

  • HyperioN
  • Registratie: April 2003
  • Laatst online: 07-10 22:02
Brynnie schreef op maandag 6 februari 2017 @ 20:16:
Het enige verschil met mijn oplossing is bij jou het woordje var op lijn 2. Bedoel je dat er door var te zetten, telkens een nieuwe variable wordt aangemaakt voor die scope?

Ik heb je oplossing net geprobeerd en ook daarbij krijg ik bij elke alert enkel het data-tlt van de laatste H (H9 in bovenstaand geval).
Je hebt gelijk, sorry. Ben niet volledig genoeg geweest.
Een for-loop maakt geen block-scope want zoiets bestaat niet in Javascript. Zie bijv; http://stackoverflow.com/...cript-loop-variable-scope

Dat kun je als het ware forceren door het te wrappen in een functie. Je krijgt dan "function scope" waardoor de variabelen i en element niet "meeveranderen" in je for-loop.
JavaScript:
1
2
3
4
5
6
7
8
9
10
for (var i = 1; i < 10; i++) {
    var element = document.getElementById("H" + i);
    if(typeof element !== 'undefined' && element !== null) {
      (function(i, element) {
      element.onclick = function () {
          alert('H' + i + ': ' + element.getAttribute('data-tlt'));
       }
      })(i, element);
   };
}

Dat zou moeten werken..

[ Voor 4% gewijzigd door HyperioN op 06-02-2017 20:24 ]


Acties:
  • 0 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 17:09

orf

Wat er mis gaat: Er wordt in een loop aan i 1 t/m 9 toegekend. Als die loop volledig gedaan is, bevat de variabele "i" het cijfer 9. Als je dan later op een element clickt, zie je de laatste waarde die "i" nu heeft.

Wel is het een goed idee om variabelen die je assignt een "var" mee te geven. Daarmee zorg je ervoor dat de variabele binnen de huidige scope ge-assignd wordt (in plaats van global).

Je kunt de variabele toewijzen aan het element:

JavaScript:
1
2
3
4
for (var i = 1; i < 10; i++) {
        var element = document.getElementById("H" + i);
        element.i = i;
    }


Nu heeft het element zelf de juiste waarde.
Binnen de scope van de click handler, kun je er dan met "this" bij. Ik ben niet zo'n voorstander van complete voorbeelden, maar dit laat het wel zien:

JavaScript:
1
2
3
4
5
6
7
for (var i = 1; i < 10; i++) {
        var element = document.getElementById("H" + i);
        element.i = i;
        element.onclick = function () {
            alert('H' + this.i + ': ' + this.getAttribute('data-tlt'));
        };
    }


Je ID's hebben wel een beetje gekke namen. Gaat het hier om headings?

Acties:
  • 0 Henk 'm!

  • HyperioN
  • Registratie: April 2003
  • Laatst online: 07-10 22:02
orf schreef op maandag 6 februari 2017 @ 20:24:

JavaScript:
1
2
3
4
5
6
7
for (var i = 1; i < 10; i++) {
        var element = document.getElementById("H" + i);
        element.i = i;
        element.onclick = function () {
            alert('H' + this.i + ': ' + this.getAttribute('data-tlt'));
        };
    }
QFT dit is nog veel mooier dan de oplossing in mijn post :)

Acties:
  • 0 Henk 'm!

  • johnkeates
  • Registratie: Februari 2008
  • Laatst online: 04-07 16:30
Is deze code niet server-side door een templating/scripting taal gegenereerd?

Acties:
  • 0 Henk 'm!

  • b2vjfvj75gjx7
  • Registratie: Maart 2009
  • Niet online
Iedereen voorbij aan het feit dat H6+ niet bestaat?

http://stackoverflow.com/a/22638901/5118952

Los daarvan kan je natuurlijk een universele name-selector maken, iets als [id*=H]

- edit - te snel :) Het is geen H1 / H6 - maar #H{x}

[ Voor 18% gewijzigd door b2vjfvj75gjx7 op 06-02-2017 20:31 ]


Acties:
  • +1 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 17:09

orf

b2vjfvj75gjx7 schreef op maandag 6 februari 2017 @ 20:30:
Iedereen voorbij aan het feit dat H6+ niet bestaat?

http://stackoverflow.com/a/22638901/5118952

Los daarvan kan je natuurlijk een universele name-selector maken, iets als [h*]
getElementById. ;)

Acties:
  • 0 Henk 'm!

  • Brynnie
  • Registratie: Februari 2001
  • Niet online
johnkeates schreef op maandag 6 februari 2017 @ 20:27:
Is deze code niet server-side door een templating/scripting taal gegenereerd?
Code (html) is inderdaad serverside gegenereerd. Er moeten achteraf wel nog click-events aan gekoppeld worden. Jammer genoeg kan je serverside geen javascript mee opnemen in de gegenereerde html omdat die clientside niet uitgevoerd wordt. Dat moet dus nadien, waarbij de data die nodig is voor de click-events doorgegeven wordt adhv data attributes bij de html elementen.

Acties:
  • 0 Henk 'm!

  • b2vjfvj75gjx7
  • Registratie: Maart 2009
  • Niet online
Brynnie schreef op maandag 6 februari 2017 @ 20:34:
[...]


Jammer genoeg kan je serverside geen javascript mee opnemen in de gegenereerde html omdat die clientside niet uitgevoerd wordt.
Bedoel je dat je geen events kan binden aan elementen die nog niet in de DOM bestaan op het moment dat ze gedefinieerd worden?

Want dat kan wel;

https://plainjs.com/javas...inding-event-handlers-14/

Of via jQuery;

http://stackoverflow.com/a/1207393/5118952

Acties:
  • 0 Henk 'm!

  • q-enf0rcer.1
  • Registratie: Maart 2009
  • Laatst online: 09-10 14:44
Ook nog een leuke is om in plaats van de ouderwetse for loop, de iets modernere forEach te gebruiken:
var array = Array.apply(null, Array(9));
array.forEach(function(item,index){
// code van één van de voorbeelden hier, wel nog +1 op index plaatsen
});

Acties:
  • 0 Henk 'm!

  • q-enf0rcer.1
  • Registratie: Maart 2009
  • Laatst online: 09-10 14:44
Brynnie schreef op maandag 6 februari 2017 @ 20:34:
[...]


Code (html) is inderdaad serverside gegenereerd. Er moeten achteraf wel nog click-events aan gekoppeld worden. Jammer genoeg kan je serverside geen javascript mee opnemen in de gegenereerde html omdat die clientside niet uitgevoerd wordt. Dat moet dus nadien, waarbij de data die nodig is voor de click-events doorgegeven wordt adhv data attributes bij de html elementen.
Dat is dan een fout in jullie back-end. JavaScript gegenereerd door de serverside wordt namelijk 100% uitgevoerd op de clientside mits correct geimplementeerd. De clientside krijgt tenslotte gewoon een HTML pagina geserveerd door de serverside. Of dat nou via PHP of ASP is maakt verder niet uit.

[ Voor 9% gewijzigd door q-enf0rcer.1 op 07-02-2017 21:03 ]


Acties:
  • 0 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 17:09

orf

q-enf0rcer.1 schreef op dinsdag 7 februari 2017 @ 20:59:
Ook nog een leuke is om in plaats van de ouderwetse for loop, de iets modernere forEach te gebruiken:


[...]
Met welk voordeel? Het is trager en er is geen array in dit geval. Dan kun je starten met een lege array (zoals in je voorbeeld) en zelf een iterator bijhouden, maar dat maakt het niet mooier of beter, toch?

Je kan ook nog een while(i--){} gebruiken. Dat is nog iets sneller, maar dan tel je wel achteruit, wat niet altijd handig is.

Acties:
  • 0 Henk 'm!

  • q-enf0rcer.1
  • Registratie: Maart 2009
  • Laatst online: 09-10 14:44
orf schreef op dinsdag 7 februari 2017 @ 21:07:
[...]


Met welk voordeel? Het is trager en er is geen array in dit geval. Dan kun je starten met een lege array (zoals in je voorbeeld) en zelf een iterator bijhouden, maar dat maakt het niet mooier of beter, toch?

Je kan ook nog een while(i--){} gebruiken. Dat is nog iets sneller, maar dan tel je wel achteruit, wat niet altijd handig is.
Het voordeel is met name de leesbaarheid, maar in het vorige voorbeeld kwam dat wellicht nog niet zo goed naar voren. Hier een verbetering:

JavaScript:
1
2
3
4
5
6
var array = document.querySelectorAll("[data-tlt]"); // hier stoppen we alle elementen met het data-tlt attribuut in een array
array.forEach(function(item,index){ // hier loopen we over alle elementen met het data-tlt attribuut
    item.onclick = function () {
        alert('H' + (index+1) + ': ' + item.getAttribute('data-tlt')); // we weten zeker dat het data-tlt attribuut aanwezig is vanwege de query eerder, daarnaast is er geen scope issue met deze loop
    };
});


Ik vermoed dat dit ook nog eens sneller is omdat je maar eenmalig een DOM-query hoeft uit te voeren.

[ Voor 59% gewijzigd door q-enf0rcer.1 op 07-02-2017 21:51 ]


Acties:
  • +1 Henk 'm!

  • orf
  • Registratie: Augustus 2005
  • Laatst online: 17:09

orf

q-enf0rcer.1 schreef op dinsdag 7 februari 2017 @ 21:12:
[...]
JavaScript:
1
2
3
4
5
6
var array = document.querySelectorAll("[data-tlt]");
array.forEach(function(item,index){
    item.onclick = function () {
        alert('H' + (index+1) + ': ' + item.getAttribute('data-tlt'));
    };
});
Dit werkt, maar let wel op dat document.querySelectorAll een NodeList returned en niet een array. Support om met een forEach te iteraten op een NodeList is veel later gekomen dan voor een Array. Dat kan voor JS errors in IE, Safari <10 en op Android devices zorgen.

Acties:
  • 0 Henk 'm!

  • q-enf0rcer.1
  • Registratie: Maart 2009
  • Laatst online: 09-10 14:44
orf schreef op dinsdag 7 februari 2017 @ 21:54:
[...]


Dit werkt, maar let wel op dat document.querySelectorAll een NodeList returned en niet een array. Support om met een forEach te iteraten op een NodeList is veel later gekomen dan voor een Array. Dat kan voor JS errors in IE, Safari <10 en op Android devices zorgen.
Scherp! Browser compatibility is inderdaad wel een punt, dat is uiteraard een afweging die moet worden gemaakt.

Je kunt eventueel iets meer zekerheid inbouwen door de Array.from() method te gebruiken, zie:
https://developer.mozilla...Global_Objects/Array/from

Acties:
  • 0 Henk 'm!

  • Brynnie
  • Registratie: Februari 2001
  • Niet online
q-enf0rcer.1 schreef op dinsdag 7 februari 2017 @ 21:01:
[...]


Dat is dan een fout in jullie back-end. JavaScript gegenereerd door de serverside wordt namelijk 100% uitgevoerd op de clientside mits correct geimplementeerd. De clientside krijgt tenslotte gewoon een HTML pagina geserveerd door de serverside. Of dat nou via PHP of ASP is maakt verder niet uit.
De html waarop de javascript "losgelaten" moet worden, wordt gegenereerd door json gegevens op te halen dmv een asyncrhone ajax call die getriggerd wordt afhankelijk van de userinteractie op de pagina, dus nadat het DOM gegenereerd werd.

De voorgestelde for loop (met this, van Orf) in een van de vorige posts werkt prima als oplossing.

Aan allen hartelijk dank voor het constructief meedenken.

Acties:
  • 0 Henk 'm!

  • q-enf0rcer.1
  • Registratie: Maart 2009
  • Laatst online: 09-10 14:44
Brynnie schreef op dinsdag 7 februari 2017 @ 23:19:
[...]


De html waarop de javascript "losgelaten" moet worden, wordt gegenereerd door json gegevens op te halen dmv een asyncrhone ajax call die getriggerd wordt afhankelijk van de userinteractie op de pagina, dus nadat het DOM gegenereerd werd.

De voorgestelde for loop (met this, van Orf) in een van de vorige posts werkt prima als oplossing.

Aan allen hartelijk dank voor het constructief meedenken.
Dat maakt het zeker wat moeilijker! Dit gaat misschien een beetje buiten de scope van je vraag, maar wellicht interessant om te weten:

Afhankelijk van wat je wilt doen zijn er nog steeds mogelijkheden, bijvoorbeeld met hulp van de Observable of Promise patronen om code na async calls uit te voeren. Daarnaast is het tevens mogelijk om event listeners aan te maken voor dynamisch toegevoegde elementen, zie bijvoorbeeld: https://api.jquery.com/on/#on-events-selector-data-handler
Pagina: 1