Percentage in een grafiek

Status
Niet open voor verdere reacties.

cboetens

Gebruiker
Lid geworden
4 jan 2012
Berichten
24
Hallo iedereen,

Ik ben nu al een tijdje bezig aan een oefening en er zit een foutje in die ikzelf maar niet kan zijn en waarbij ik wel wat hulp kan gebruiken. Het programma'tje zit als volgt in elkaar

HTML:

HTML:
<!doctype html>
<!-- In dit bestand mag niets gewijzigd / toegevoegd / verwijderd worden -->
<html lang="nl">
<head>
	<meta charset="UTF-8">
	<title>Verkiezingen 2014</title>
	<script src="verkiezingen.js"></script>
	<link rel="stylesheet" href="verkiezingen.css"/>
</head>
<body>
<!-- In dit bestand mag niets gewijzigd / toegevoegd / verwijderd worden -->
<table></table>
<div id="percentage"></div>
<div id="grafiek"></div>
<!-- In dit bestand mag niets gewijzigd / toegevoegd / verwijderd worden -->
</body>
</html>

In de html mag (zoals je kan zien) niets gewijzigd worden. Alles dient dus gemaakt te worden met Javascript.

Code:
 /*
 *
 * Naam: Cédric Boetens
 * Groep: 1NMCT1
 *
 */

arrPartijen = new Array("Partijen","Groen","Vlaams Belang","OpenVld","U F","sp.a","N-VA","CD&V","PVDA+","Andere");
var arr14 = new Array(2014,365779,248840,594464,34741,587901,1339943,860685,106114,63968);
var arr09 = new Array(2009,278211,628564,616610,47319,627852,537040,939873,42849,394007);
var totaal14 = 4266403;
var table, grafiek, percentage;
var intervalleke;


 //Function Init start
 window.addEventListener('load',init, false);

function init(){
    console.log("init initiated...")
    table= document.getElementsByTagName('table')[0];
    grafiek= document.getElementById('grafiek');
    percentage= document.getElementById('percentage')
    maakTabel();
    maakGrafiek()
}

function maakTabel(){
    console.log("making table...");
    maakRij(arrPartijen);
    maakRij(arr09);
    maakRij(arr14);



}

function maakRij(arr){
    //Maak voor iedere array een aparte tablerow aan
    var tr= document.createElement("tr");
    //In de tableheading wordt nu het eerste element uit de ingelezen array getoond
    tr.innerHTML= "<th>" + arr[0] + "</th>";
    for(var i=1; i < arr.length; i++){
        //Nu wordt voor iedere array de gegevens ingelezen om ze in de tabel te plaatsen.
        tr.innerHTML= tr.innerHTML + "<td>" + arr[i] + "</td>";


    }
    table.appendChild(tr);





}

function maakGrafiek(){
    console.log("making graph...");

    // Schrijf hier de code om de grafiek aan te maken.
    // De grafiek bestaat uit divs met een hoogte gelijk aan het aantal stemmen gedeeld door 5000.
    // De breedte van elke balk is 50px.
    // De divs in de grafiek zijn reeds gepositioneerd door de CSS, maar hebben nog een left positie nodig.
    // Voor de kleur zijn er in de CSS klasses voorzien.
    // Zorg ook voor een attribuut 'data-value' het aantal stemmen van de partij bevat.
    // Bij het klikken op een balk wordt het percentage dat een partij gescoord heeft,
    // getoond in de percentage div.
    for(var i= 1; i< arrPartijen.length; i++){
        //div
        var div= document.createElement("div");
        //Breedte van elke balk is 50px;
        div.style.width= '50px';
        //De hoogte = het aantal stemmen van de partij / 5000
        div.style.height= Math.floor(parseInt(arr14[i]/5000)) + 'px';
        div.style.left= 60*(i+1) + 'px';
        //Attribuut data-value met daarin de stemmen
        div.setAttribute("data-value",arr14[i]);
        console.log(div.getAttribute("data-value"));
        //Plaats hier de code voor de classname
        div.className= arrPartijen[i].substr(0,1).toLowerCase();
        grafiek.appendChild(div);
        console.log("hier zijn we toch al");
        //als er op de balk geklikt wordt moet de score erboven komen in div
        if(div.addEventListener){
            //verwijzing naar de functie toonpercentage
            div.addEventListener('click',toonPercentage(),false);


        }

    }


function toonPercentage(){
    // Indien het percentage wordt getoond, moet de opacity van de percentagediv eerst op 0 gezet worden.
    // Nadien moet de div getoond worden door de opacity te verhogen door een timer.
    // De achtergrond van de div wordt dezelfde als die van de partij waarop geklikt werd.

    var balkje = getTargetElement(this);
    console.log(balkje);
    if (balkje.id !== "grafiek") {
        percentage.className = balkje.className;
        var pct = parseInt(balkje.getAttribute("data-value"));
        pct += pct / totaal14 * 100;
        pct = pct.toFixed(2);
        percentage.innerText = pct + '%';
        percentage.style.opacity = 0;
        intervalleke = window.setInterval(makeVisible, 50);
    } else {
        percentage.className = "";
        percentage.innerHTML = "";
    }


    }
}


function makeVisible(){
    var currentOpacity = percentage.style.opacity;
    var newOpacity= parseFloat(currentOpacity) + 0.05;
    percentage.style.opacity= newOpacity;
    if(newOpacity > 1.0){

        window.clearInterval(intervalleke);

    }

}

 function getTargetElement(evt) {
    if (evt.target && typeof(evt.target) !== "undefined") {
        return evt.target;
    } else {
        evt = window.event; //global event in IE
        return evt.srcElement;
    }



}

Nu wat is de bedoeling, allereerst krijg ik drie arrays met daarin data rond de afgelopen verkiezingen in België. Het eerste punt in het programmatje is met Javascript een tabel opmaken met daarin mooi de waardes van die arrays. Maar daarnaast is het ook de bedoeling dat er onder de tabel een grafiek wordt gemaakt adhv de data uit die arrays (dus bijvoorbeeld de partij NVA heeft 345000 stemmen, voor de grafiek maken we dan een div in de kleur geel (een rechtopstaande balk dus) in verhouding met al de rest van de stemmen.

Klik je op zo'n balkje? Dan krijg je een div boven de grafiek te zien in hetzelfde kleurtje als de balk met daarin het aantal stemmen (de opacity van die div moet dus omhoog).

Het grootste deel van dit programma is mij al gelukt maar als ik het percentage wil tonen in de div boven de grafiek loop ik vast met de error (uncaught typeerror: undefined is not a function). Meer bepaald bij volgend stukje code:

HTML:
function toonPercentage(){
    // Indien het percentage wordt getoond, moet de opacity van de percentagediv eerst op 0 gezet worden.
    // Nadien moet de div getoond worden door de opacity te verhogen door een timer.
    // De achtergrond van de div wordt dezelfde als die van de partij waarop geklikt werd.

    var balkje = getTargetElement(this);
    console.log(balkje);
    if (balkje.id !== "grafiek") {
        percentage.className = balkje.className;
        [B]var pct = parseInt(balkje.getAttribute("data-value"));[/B] --> uncaught type-error: undefined is not a function.
        pct += pct / totaal14 * 100;
        pct = pct.toFixed(2);
        percentage.innerText = pct + '%';
        percentage.style.opacity = 0;
        intervalleke = window.setInterval(makeVisible, 50);
    } else {
        percentage.className = "";
        percentage.innerHTML = "";
    }


    }
}

Weet er iemand hoe dit euvel kan opgelost worden, het is waarschijnlijk iets heel doms. Maar ik kijk er jammer genoeg over.

Alvast bedankt op voorhand aan diegene die dit kan vinden!

Cédric Boetens
 
Dag Cedric,

Ik heb jouw html + javascript even lokaal gedraaid en kan je de volgende hints meegeven (aangezien het hier duidelijk een opdracht betreft van een opleiding ga ik je niet domweg een werkende versie geven, maar genoeg info zodat je het zelf kan oplossen, zo leer je er meer van):

De meeste browsers hebben zeer goede javascript debuggers ingebouwd; in mijn browser kon ik op de foutmelding dubbelklikken en kwam ik inderdaad bij jouw gemarkeerde regel terecht. Toen heb ik een 'breakpoint' op die regel ingesteld: dat wil zeggen dat de browser de code tot aan die lijn zal uitvoeren, en dan pauzeren. Als je niet weet hoe dit in jouw browser moet, vraag maar even en dan tonen wij jou dat wel.
Vervolgens heb ik de pagina herladen.

Wanneer de debugger op het breakpoint komt, kan je kijken naar de waardes van variabelen die je op dat moment interesseren. Op de bewuste lijn staan er twee functies, waarvan er volgens de foutmelding 1 undefined zou zijn. Aangezien 1 van de functies een methode is op een object dat we zelf invullen (balkje), is dat dus ook het object waar we naar gaan kijken. Ik denk dat je een beetje verrast zal zijn van het resultaat.

De volgende stap is (nog steeds op het breakpoint gepauzeerd) te kijken naar de variabele (this) waarmee balkje wordt opgevuld; ook dat is niet wat je zou verwachten (of waar je op hoopt).

Wanneer de waarde van 'this' niet is wat je verwacht, wil dat zeggen dat je functie uitgevoerd wordt vanaf een locatie die je niet voorzien hebt / wenst. De volgende stap is dan in je broncode opzoeken waar die functie allemaal voorkomt (je hebt geluk, maar op 1 plaats!).

Jouw functie probeer je mee te geven als argument/parameter aan een andere functie. Zoek nog eens op hoe je de argumenten aan die specifieke functie moet meegeven ;)


Als er iets in deze uitleg niet goed duidelijk is, vraag gerust. Ikzelf ben vanaf morgen tot na het weekend afwezig, dus ofwel moet je tot dan wachten op antwoord, ofwel helpt een van de andere helpmijers jou vast wel verder op weg!
 
Laatst bewerkt:
Dag Cedric,

Ik heb jouw html + javascript even lokaal gedraaid en kan je de volgende hints meegeven (aangezien het hier duidelijk een opdracht betreft van een opleiding ga ik je niet domweg een werkende versie geven, maar genoeg info zodat je het zelf kan oplossen, zo leer je er meer van):

De meeste browsers hebben zeer goede javascript debuggers ingebouwd; in mijn browser kon ik op de foutmelding dubbelklikken en kwam ik inderdaad bij jouw gemarkeerde regel terecht. Toen heb ik een 'breakpoint' op die regel ingesteld: dat wil zeggen dat de browser de code tot aan die lijn zal uitvoeren, en dan pauzeren. Als je niet weet hoe dit in jouw browser moet, vraag maar even en dan tonen wij jou dat wel.
Vervolgens heb ik de pagina herladen.

Wanneer de debugger op het breakpoint komt, kan je kijken naar de waardes van variabelen die je op dat moment interesseren. Op de bewuste lijn staan er twee functies, waarvan er volgens de foutmelding 1 undefined zou zijn. Aangezien 1 van de functies een methode is op een object dat we zelf invullen (balkje), is dat dus ook het object waar we naar gaan kijken. Ik denk dat je een beetje verrast zal zijn van het resultaat.

De volgende stap is (nog steeds op het breakpoint gepauzeerd) te kijken naar de variabele (this) waarmee balkje wordt opgevuld; ook dat is niet wat je zou verwachten (of waar je op hoopt).

Wanneer de waarde van 'this' niet is wat je verwacht, wil dat zeggen dat je functie uitgevoerd wordt vanaf een locatie die je niet voorzien hebt / wenst. De volgende stap is dan in je broncode opzoeken waar die functie allemaal voorkomt (je hebt geluk, maar op 1 plaats!).

Jouw functie probeer je mee te geven als argument/parameter aan een andere functie. Zoek nog eens op hoe je de argumenten aan die specifieke functie moet meegeven ;)


Als er iets in deze uitleg niet goed duidelijk is, vraag gerust. Ikzelf ben vanaf morgen tot na het weekend afwezig, dus ofwel moet je tot dan wachten op antwoord, ofwel helpt een van de andere helpmijers jou vast wel verder op weg!


Hallo !

Eerst en vooral, bedankt voor je snelle reactie! Ik heb jouw advies eens opgevolgd en inderdaad eens gaan debuggen met de console in chrome (vrij snel gevonden hoe je breakpoints enzo moest plaatsen). En hiermee heb ik eigenlijk opgemerkt dat er in balkje vrijwel steeds kwam te staan dat het targetelement document was en het moest div zijn. Wat heb ik dan gedaan?

In de addeventlistener erboven heb ik een aantal dingen aangepast volgens de regels van w3schools en dan zag de addeventlistener er als volgt uit:

Code:
if(div.addEventListener){
            //verwijzing naar de functie toonpercentage
            div.addEventListener('click',function(event){toonPercentage(event)},false);


        }

Vervolgens heb ik het over een andere boeg gegooid in de daarop volgende functie (na een parameter "event" te hebben toegevoegd aan de functie) en heb ik geprobeerd om aan balkje al volgt het juist event toe te kennen:

Code:
var balkje = evt.srcElement;

En toen werkte het wel! Mag ik hier dan uit opmaken dat ik met de 'oude' code eigenlijk nooit het juist event ging kunnen laten toekennen omdat er simpelweg bij de functie geen parameter werd meegegeven en het hierdoor onmogelijk was om het juiste event te weten te komen?

Nogmaals bedankt voor je snelle reactie!

Groetjes,
Cédric Boetens
 
Jouw oplossing werkt, ook al is ze anders dan die waar ik op aanstuurde, maar dat is zeker ook OK!

Wat er echt 'fout' liep in je oorspronkelijke code, was dat je aan addEventListener helemaal niet de functie meegaf die je wenste uit te voeren, maar het resultaat ervan. In je code stond: div.addEventListener('click',toonPercentage(),false); en niet div.addEventListener('click',toonPercentage,false); (zonder de haakjes).
Jij vroeg de browser dus om de functie bij het laden van de pagina uit te voeren en het resultaat ervan te linken aan het click-event, door gewoonweg de haakjes weg te laten, geef je wel de functie zelf mee. Vergelijk ook even met die andere plaats waar addEventListener staat, helemaal in het begin om de init functie aan het onload event te linken.

In jouw oplossing maak je gebruik van een "wrapper function" om het event op te vangen en aan je eigen functie te kunnen doorgeven. Dat is OK, maar eigenlijk ook helemaal niet nodig, omdat de browser dat stiekem al voor jou doet. Wel moet je in je eigen functie dan ook een parameter voorzien om dat event op te vangen (net zoals je nu al deed).
Wanneer je dus een functie maakt die je aan een bepaald event wenst te koppelen, wordt het eerste argument van die functie altijd gebruikt om het event af te vangen.
Een klein voorbeeldje:
HTML:
<div id="testklik">klikkerdeklik</div>
<script>
function ikGaReagerenOp(hetEvent)
{
    console.log('Ik reageer op een ' + hetEvent.type + ' event');
}

var deDiv = document.getElementById('testklik');
deDiv.addEventListener('click', ikGaReagerenOp);
deDiv.addEventListener('mousedown', ikGaReagerenOp);
</script>
Zoals je kan zien heb ik ook de 3de parameter niet ingevuld voor addEventListener, deze is by default 'false'.

De lijn waarop je balkje dan afleid van het event was in de originele code wel iets beter dan hoe je het nu doet, aangezien je originele code een functie opriep (getTargetElement) die een aantal browserverschillen voor je opvangt; srcElement bestaat namelijk niet in elke browser (of is niet overal hetzelfde ingevuld).
Wat je wel diende aan te passen was dat je getTargetElement niet met 'this' maar met je event-parameter moet aanroepen.

In jouw situatie kan je zelfs rechtstreeks 'this' gebruiken ipv balkje, aangezien this contextgevoelig is en voor eventListeners standaard verwijst naar het Element waarop het event getriggered werd (in jouw geval de div waarop er geklikt werd). Hou er wel rekening mee dat er een aantal manieren bestaan om de waarde van this te wijzigen dmv binding/call/apply, maar dat is een gedeelte van javascript wat meestal niet in basisopleidingen voorkomt ;)

Samenvatting:
  1. Jouw oplossing is OK
  2. Het kon eenvoudiger door gewoonweg de haakjes weg te laten bij de parameter van addEventListener zodat de functie zelf werd meegegeven en niet het resultaat ervan.
  3. Je functie werd aangepast om het event te kunnen opvangen; het event zelf wordt automatisch door de browser ingevuld als de eerste parameter aan je functie.
  4. de functie getTargetElement(evt) is een veiligere optie dan evt.srcElement, omdat die een aantal browserverschillen afvangt
 
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan