Was ist da schief (JS)?
Starte ich das Programm so beginnt es so wie ich es will. Irgendwann wird dann aber ein Fehler gezeigt
("main.js:27 Uncaught TypeError: Cannot read properties of null (reading 'setAttribute')
at ClickIt (main.js:27)
at clickvol2 (main.js:21)
at HTMLParagraphElement.<anonymous> (main.js:30))
und mehrere Flächen farbig markiert und man muss manchmal doppelt klicken. Wo liegt hier der Fehler?
JS:
"use strict";
let genNum = function() {
let gen = Math.random() * 9;
let num = Math.round(gen);
let numers = ["null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun"];
let final = (numers[num]);
if(final === "zehn") {
genNum()
}
return final;
};
let i = 0;
document.getElementById("Punkteanzahl").textContent = i;
let clickvol2 = function() {
if (document.querySelector("#eins", "#zwei", "#drei", "#vier", "#fünf", "#sechs", "#sieben", "#acht", "#neun").hasAttribute("class")) {
console.log(".")
} else {
ClickIt(genNum())
}};
let ClickIt = function(num) {
let hash = "#";
let ident = hash.concat(num);
document.querySelector(ident).setAttribute("class", "click");
document.querySelector(ident).addEventListener("click", function() {
document.querySelector(ident).removeAttribute("class");
clickvol2();
i++;
})
};
ClickIt(genNum());
console.log(document.getElementById("Punkteanzahl").textContent)
2 Antworten
Guten Abend,
Beim ersten Ansehen ist mir folgendes aufgefallen:
if (document.querySelector("#eins", "#zwei", "#drei", "#vier", "#fünf", "#sechs", "#sieben", "#acht", "#neun").hasAttribute("class")) {...}
Probiere das mal mit document.querySelectorAll() - Diese Funktion kann mehrere Parameter (in diesem Falle Selektoren) aufnehmen und verarbeiten, was bei document.querySelector() meines Erachtens nicht der Fall ist.
Der Rückgabewert von document.querySelectorAll() ist vom Typ NodeList.
Über diese Liste müsstest du nochmal mit forEach() iterieren. Möchtest du überprüfen, ob eines der Elemente die genannte Klasse hat oder alle?
Eines dieser Elemente könnte evt die Kalsse noch haben. Wenn mans so sieht ist es ne Sicherheit falls in "ClickIt" am Ende der Funktion aus welchen Gründen auch immer die Klasse "click" mit dem Design nicht removed wird.
Also möchtest du damit erstmal überprüfen, ob eines der Elemente das Attribut class hat?
Es ist ne bestimmte Klasse aber "class" reicht da die Elemente max diese eine haben und sonst nur jeweils eine ID über die ich sie ansteuere.
Probiere vielleicht mal das:
document.querySelectorAll(...).forEach(element => {
if (element.hasAttribute('class')) {
...
}
});
Punkte durch Argumente und Code ersetzen.
Noch ein Tipp nebenbei:
Wenn du mit Klassen arbeitest, dann arbeite nicht mit dem class Attribut, sondern überprüfe mithilfe der Eigenschaft classList.
Ich glaube ich habe mich irgendwie vertippt.... Fehler ist: "Uncaught SyntaxError: Lexical declaration cannot appear in a single-statement context" und neuer Code-Schnipsel:
let clickvol2 = function() {
if (document.querySelectorAll("#eins", "#zwei", "#drei", "#vier", "#fünf", "#sechs", "#sieben", "#acht", "#neun").forEach(element => {
if (element.hasAttribute('class')) {
console.log(".")
} else {
ClickIt(genNum())
}
}))
Lass mal das if weg, die Überprüfung findet in der Schleife statt.
Und in der Schleife den Parameter bitte element und nicht e nennen.
Immerhin keine Fehler mehr aber jetzt wird die Klasse nicht mehr zugeordnet und so ist nichts gefärbt....
let genNum = function() {
let gen = Math.random() * 9;
let num = Math.round(gen);
let numers = ["null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun"];
let final = (numers[num]);
if(final === "zehn") {
genNum()
}
return final;
};
let i = 0;
document.getElementById("Punkteanzahl").textContent = i;
let clickvol2 = function() {
document.querySelectorAll("#eins", "#zwei", "#drei", "#vier", "#fünf", "#sechs", "#sieben", "#acht", "#neun").forEach(element => {
if (element.hasAttribute('class')) {
console.log(".")
} else {
ClickIt(genNum())
}
})
let ClickIt = function(num) {
let hash = "#";
let ident = hash.concat(num);
document.querySelector(ident).setAttribute("class", "click");
document.querySelector(ident).addEventListener("click", function() {
document.querySelector(ident).removeAttribute("class");
clickvol2();
i++;
})
};
ClickIt(genNum());
console.log(document.getElementById("Punkteanzahl").textContent)}
Könntest du mir vielleicht kurz sagen, was genau der Algorithmus machen soll? Ich würde ihn dann mal umsetzen und hier posten.
Er soll 9 paragraphs rndm eine Klasse zuweisen die per css mit einem Design verbunden ist. Klickt man dieses gefärbte Feld an soll sich die Variable "i" erhöhen, die Klasse wieder removed und die Funktion erneut ausgeführt (am besten 3 Mal) werden. Hier der komplette Code:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Steal-botprotection</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="Punkte">
<p id="Punkteanzahl"></p>
</div>
<div class="box">
<p id="eins"></p>
<p id="zwei"></p>
<p id="drei"></p>
<p id="vier"></p>
<p id="fünf"></p>
<p id="sechs"></p>
<p id="sieben"></p>
<p id="acht"></p>
<p id="neun"></p>
</div>
<script src="main.js"></script>
</body>
</html>
JS:
"use strict";
let genNum = function() {
let gen = Math.random() * 9;
let num = Math.round(gen);
let numers = ["null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun"];
let final = (numers[num]);
if(final === "zehn") {
genNum()
}
return final;
};
let i = 0;
document.getElementById("Punkteanzahl").textContent = i;
let clickvol2 = function() {
document.querySelectorAll("#eins", "#zwei", "#drei", "#vier", "#fünf", "#sechs", "#sieben", "#acht", "#neun").forEach(element => {
if (element.hasAttribute('class')) {
console.log(".")
} else {
ClickIt(genNum())
}
})
let ClickIt = function(num) {
let hash = "#";
let ident = hash.concat(num);
document.querySelector(ident).setAttribute("class", "click");
document.querySelector(ident).addEventListener("click", function() {
document.querySelector(ident).removeAttribute("class");
clickvol2();
i++;
})
};
ClickIt(genNum());
console.log(document.getElementById("Punkteanzahl").textContent)}
CSS:
* {
background-color: #0e163d;
}
.box {
display: grid;
background-color: #1e2859;
margin: 0.5%;
grid-template-columns: 33.33% 33.33% 33.33%;
grid-template-rows: 30vh 30vh 30vh;
column-gap: 0.6vh;
row-gap: 0.6vh;
}
#Punkte {
background-color:#ffffff;
height: 3vh;
margin: 1%;
}
#Punkteanzahl {
text-align: center;
font-family: sans-serif;
background: #ffffff;
}
.click {
background-color: red;
}
Variablen Namen sollte man daran anpassen wie häufig sie verwendet werden. Wenn so was wie e nur für einen Block von 4 Zeilen relevant ist und die Bedeutung klar ist, wie bei .forEach, ist das ein komplett valider Variablenname. Erzähl keinen Quatsch.
Ich habe das nicht gesagt, weil es kein gültiger Variablenname wäre, Ich habe es in meinem Beispiel nur deutlicher als element bezeichnet, damit das Ansinnen verständlicher wird. Dass der Compiler nicht weiß, dass e nicht element ist, brauch ich dir wohl nicht zu erklären.
Dann nehm ichs zurück, geht nur aus der Historie nicht so klar hervor, denke wurde nachträglich editiert.
Danek werd ich Mal ausprobieren... Hast du vielleicht sonst noch eine Idee?
Offensichtlich wird das Element nicht gefunden.
Cannot read properties of null (reading 'setAttribute')
Du versuchst .setAttribute auf einen null wert anzuwenden.
Du kannst über die Browser Tools den Code debuggen um raus zu finden bei welchem Parameter der an die Funktion übergeben wird der Fehler auftritt. Ansonsten kannst du auch mit console.log das ausgeben oder so.
Debuggen mit Firefox und Chrome:
https://developer.mozilla.org/en-US/docs/Tools/Debugger
https://developer.chrome.com/docs/devtools/javascript/
tl;dr es wird halt ein Element nicht gefunden.
PS:
let gen = Math.random() * 9;
let num = Math.round(gen);
let numers = ["null", "eins", "zwei", "drei", "vier", "fünf", "sechs", "sieben", "acht", "neun"];
ist Quatsch. Definier den Array und nutze let num = Math.floor(Math.random() * numers.length)
Du willst nicht aufrunden, das Maximale Ergebnis darf höchstens 8 sein da Arrays bei 0 anfangen.
Nachdem ein anderer User mir nun geholfen hat wird kein Efhler mehr engazeigt... Dafür passiert nichts mehr...
Ich kann dir helfen wenn du deinen Code irgendwo vernünftig hochlädst, so das man ihn ausführen kann und das Problem nachvollziehen. Also inklusive HTML, auf Pastebin, git oder ähnlichem.
Ansonsten schau dir die Links aus der Antwort an, der debugger ist die beste möglichkeit solche Fehler zu lösen.
Jo kann ich machen... Einen Moment und danke für die Hilfe....
Da passiert nichts weil du die Klammern falsch gesetzt hast. Von der einen Funktion ist die } erst ganz am Ende.
Außerdem gibts im Code das Problem was ich schon angesprochen habe, nämlich das du aufrundest.
Ein weiteres Problem ist die "#null" im Array. #null gibts im HTML nicht.
Dann scheinst du den EventListener nicht richtig zu removen das scheint auch Probleme zu machen und mehrere Felder werden rot, das willst du ja denke ich nicht. würde vermutlich einfach schauen das ich einen Event Listener auf das Div mache oder so in dem alles drinnen ist denke ich.
Der Counter an sich geht, außer das er mehrmals erhöht wird wegen dem Event Listener Problem, du schreibst ihn nur nicht ins dom wenn er aktualisiert wird.
? Dann ist es ja net mehr das was erreicht werden soll... Es soll nur etwas passieren wenn das ausgewählte Feld getippt wird...
Und was heißt Counter ins DOM schreiben (Ich weiß schon was DOM ist aber nicht in dem Zusammenhang)? Wie geht das?
Ja da wäre ich dir sehr verbunden... Vielleicht kannst du es dann auch anhand dieser erklären? Möchte ja auch was mitnehmen....
Das mit der DOM hat aufjedenfall funktioniert... KAnn ich jetzt für das nächste Mal direkt besser anwenden.
Also außer den angesprochenen Sachen wie Math.floor statt round und eben die null aus dem Array entfernt, hab ich die Functions umbennant und ein removeEventListener hinzugefügt. Die anonyme function für den listener ebenfalls entfernt und da einfach das übergeben was vorher clickvol2 oder so war.
Dafür nimmt dieser listener jetzt auch das clickEvent als Parameter an und statt auf die ID zu gehen passiert das removeAttribute einfach über das event target.
Hab außerdem den Code einfach mal automatisch formatieren lassen, macht es direkt leserlicher. Alles in eine Datei gepackt weils für Pastebin besser funktioniert.
Neuer Fehler:
"main.js:18 Uncaught TypeError: document.querySelectorAll(...).hasAttribute is not a function
at clickvol2 (main.js:18)
at HTMLParagraphElement.<anonymous> (main.js:30)"