Quiz mit Javascript erstellt - bitte um Code-Beurteilung

  • Hallo zusammen,


    um mein Selbststudium in Javascript voranzubringen, habe ich ein kleines (noch völlig unfertiges) Musik-Quiz erstellt:


    https://jsfiddle.net/m7gb8d3t/1/


    Meine Bitte ist, dass sich erfahrene Anwender meinen JS-Code anschauen und beurteilen, ob irgendetwas völlig anders gemacht werden müsste. Ich weiß, dass der Code nicht optimal ist, aber ich suche in meinem frühen Lernstadium auch noch nicht nach Perfektion – es geht mir eher um prinzipielle Fehler, die ich künftig vermeiden sollte.


    Eine Sache stört mich schon selbst: Der größte Teil des JS-Codes ist redundant – erst zum Initialisieren des Spiels, dann um mit einem Click zur nächsten Frage zu kommen. Das kann nicht der richtige Weg sein, aber ich finde derzeit keine bessere Lösung. Vielleicht habt Ihr ja einen Tipp für mich.


    Danke und Gruß

    Felix

  • Eines hast Du ja schon sehr richtig gemacht: Ein Array verwendet und nur einen Satz von Funktionen anstatt den Code zu vervielfachen wie es viele Anfänger tun.


    Was mir sofort ins Auge springt ist die Tatsache, dass sich das immer gleiche HTML im Array wiederholt. Das würde ich als erstes angehen.

    Also das HTML nur ein Mal statisch anlegen und darin die Teile, die sich verändern, mit Javascript austauschen:

    Und das Array mit den Daten so:

    JavaScript
    1. const questions = [
    2. {
    3. question: 'Welche Band veröffentlichte 1980 das Album "Paris"?',
    4. answers: ['Queen', 'Visage', 'Supertramp', 'ELO'],
    5. isCorrect: [false, false, true, false]
    6. },
    7. ];

    Wenn Du das so aufziehst, kannst Du darauf verzichten, die Eventlistener bei jedem Klick neu zu registrieren.

    Das Javascript dazu zu schreiben überlasse ich dir. Wenn Du nicht zum Ziel kommst, melde dich wieder.


    Auch in diesem Fall kann man sich andere Varianten überlegen. Was sich ebenfalls anbieten würde, ist, mit Templates zu arbeiten, d. h. im HTML Platzhalter einzubauen und diese dann gegen die aktuellen Werte auszutauschen. Für solche Platzhalter benutzt man häufig doppelte geschweifte Klammern: {{name}} weil diese normaler Weise im HTML nicht vorkommen.

    Dafür gibt es auch Bibliotheken, die es vereinfachen. Vor längerer Zeit habe ich z. B. Handlebars kennen gelernt aber ich weiß nicht, ob das noch aktuell ist.

  • Vielen Dank! Mir ist klar, dass gerade an dem generierten HTML-Code noch viel zu vereinfachen ist. Ich werde Deinen Vorschlag ausprobieren und mir auch mal den Tipp mit den Templates genauer anschauen. Ja, es muss darauf hinauslaufen, dass sich nichts wiederholt.


    Hättest Du noch eine Idee zu meinem redundanten Code? Nach Zeile 85 steht noch mal dasselbe wie oben, und das nur, weil ich die Ausgabe einmal vor dem Eventhandler brauche und dann beliebig oft danach. Das kann doch nicht der richtige Weg sein, sagt mein Bauch (während das Hirn blöd mit den Schultern zuckt).

  • Ich habe noch eine Frage zum aktuellen Code aus dem Beispiel in https://jsfiddle.net/m7gb8d3t/1/:


    Wenn man die richtige Antwort wählt, wird der Button "nächste Frage" sichtbar. Klickt man diesen, wird der Zähler i um 1 erhöht (Zeile 82 und 84).


    Ich versuche seit Stunden, den Zähler wieder auf 0 zu setzen, sobald man irgendeine Frage falsch beantwortet, aber es gelingt mir beim besten Willen nicht.


    Derzeit ist es so, dass man bei einer falschen Antwort zwar aus dem Spiel fliegt, aber mit dem erneuten Starten wieder bei der falsch beantworteten Frage landet. Ich hätte es gern so, dass man bei einer falschen Antwort immer das gesamte Spiel von Anfang an wiederholen muss. Und dazu müsste der Zähler i wieder 0 sein.


    Ist das in meinem Beispiel überhaupt machbar?

  • Ich habe nun das korrekte Beispiel hochgeladen:


    https://jsfiddle.net/zaypxtjd/


    Beantwortet man eine der Fragen falsch, wird das Spiel abgebrochen. Man hat aber auf der Fehlerseite, die erscheint, die Möglichkeit, wieder ins Spiel einzusteigen. Derzeit landet man mit einem Klick auf den Button mit der ID "repeater" jedoch nicht am Anfang des Spiels, wie ich es mir wünsche, sondern bei der zuletzt falsch beantworteten Frage. Klar, der Zähler wird ja nicht zurückgesetzt ...


    Also dachte ich mir, ich kann dem Klick auf den Repeater-Button ein i = 0; mitgeben (s. Zeile 99), aber das war eine naive Vorstellung, es funktioniert so nicht. Und auch keiner der anderen Versuche, die ich unternommen habe, funktioniert. Ich bin mit meinem Latein resp. meinem JavaScript am Ende.


    Die Frage ist also: Wie schaffe ich es, dass nach einem Fehler das Spiel von Anfang an startet, nicht mittendrin?

  • Das Problem ist, dass die Frage mit den Antworten auch neu aufgebaut werden muss, sonst läuft das i = 0; ins Leere:

    Wie ich schon in dem Kommentar geschrieben habe, schreit das natürlich danach, in eine Funktion ausgelagert zu werden, dann jetzt haben wir diesen Codeblock schon drei Mal.

  • Ganz herzlichen Dank, ich schätze Deine Hilfe sehr. Die Auslagerung in Funktionen und die Vereinfachungsschritte aus #2 sind meine nächste Aufgabe, wenn dieses Script so funktioniert, wie ich es mir wünsche.


    Mit dem Code aus #10 funktioniert es nicht, leider. Beantwortet man eine Frage falsch, wird nach dem Neustart zwar wieder die erste Frage angezeigt, aber man kommt nicht weiter – alle Funktionen sind dann außer Kraft.


    Hier ein vereinfachtes Beispiel mit nur der ersten Frage: https://jsfiddle.net/e4nroq5w/


    Es reicht, eine falsche Antwort zu geben – beim zweiten Versuch ist keine der Klickfunktionen mehr aktiv. Löscht man die Zeilen 88 bis einschließlich 97, funktionieren sie wieder. Was muss anders gemacht werden?

  • Ich bin gerade dabei, die Funktionen zu erstellen – es wird übersichtlicher. Anschließend versuche ich, die Funktionalität selbst in den Griff zu bekommen, der Nachmittag ist ja noch lang ... :)


    Nachtrag eine Stunde später ...


    Es ist geschafft – Code deutlich gestrafft und alle Funktionen wunschgemäß eingerichtet: https://jsfiddle.net/ptkv6ahL/


    Dann geht's jetzt an die Optimierung des HTML-Codes. Sempervivum Danke, danke, danke! :)

  • Sempervivum Ich hab' mal angefangen, mich mit Deinem Vorschlag aus #2 zu beschäftigen. Hier ein Fragment:



    Mir kommt dieser Ansatz viel zu kompliziert vor. Das geht sicherlich einfacher, oder?


    Einen schönen Abend wünsche ich!

  • Wenn du fertig bist, hast du lust dann meinen weg zu sehen ?

    Im großen und ganzen ändert sich nicht viel , aber beim Coden gibt es viele Wege die zum Ziel führen.


    Aber erst wenn ihr fertig seid , weil sonst wird das ein durcheinander.

    Außerdem will ich dein Lernwille da nicht stören , du bist einer von wennigen der wirklich noch lernen will und keine Komplettlösung sucht.

  • Ja, das kannst Du vereinfachen, indem Du die Elemente in einer Schleife bearbeitest:

    Ungetestet.

    Jetzt bin ich mir nicht sicher, ob das i von Zeitpunkt der Registrierung gilt oder wenn der Eventlistener feuert. Probiere es aus.

  • basti1012 Gern schaue ich mir auch Deine Variante an, vielen Dank! Dass beim Programmieren viele Wege nach Rom führen, weiß ich, bin aber froh, wenn ich erst mal einen der möglichen Wege gehen kann. Das ist alles spannendes Neuland für mich. Und ja, Lernen macht mir großen Spaß – was nutzt mir eine fertige Lösung, wenn die nächste Herausforderung dann wieder nicht zu bewältigen ist ...


    Sempervivum Ich habe Deinen Code eingebaut und glaube, ich verstehe sogar, was er tut. Darauf gekommen wäre ich allerdings nicht, muss ich zugeben. Ich beginne jetzt, die Aktionen für die Antworten einzubauen und hoffe, ich komme selbst ein Stückchen weiter. Leider habe ich heute nicht so viel Zeit dafür wie an den letzten beiden Tagen. Und natürlich wieder: herzlichen Dank!

  • Es ist geschafft! :)


    https://jsfiddle.net/t4qebu7d/1/


    Was mir nicht gefällt, ist function resetAnswer() ab Zeile 55. Hiermit setze ich nur die Hintergrundfarbe des zuvor gewählten Radiobuttons zurück. Richtig wäre es, ein uncheck anzuwenden, aber ich weiß (noch) nicht, wie ich das Element anspreche. Falls jemand einen Tipp geben möchte ...


    In jedem Fall freue ich mich gerade wie ein kleiner Junge, der ein neues Spielzeug bekommen hat. 8)


    Und ich bin sehr froh, dass ich Euch und dieses Forum gefunden habe ... kann gar nicht oft genug "Danke!" sagen.

  • Zitat

    Richtig wäre es, ein uncheck anzuwenden, aber ich weiß (noch) nicht, wie ich das Element anspreche.

    Das checked bei dem Radiobutton wegnehmen kannst Du indem Du die Eigenschaft checked auf false setzt.

    Code
    1. function resetAnswer() {
    2. var x, z;
    3. x = document.querySelectorAll('#divAnswers input[type="radio"]');
    4. for (z = 0; z < x.length; z++) {
    5. x[z].checked = false;
    6. }
    7. }

    Wenn Du gleich gezielt den Radiobutton selektierst, der gecheckt ist, brauchst Du gar keine Schleife mehr:

    Code
    1. function resetAnswer() {
    2. document.querySelector('#divAnswers input[type="radio"]:checked').checked = false;
    3. }

    (beides ungetestet)