Select Li Element on Keyup
apfelsine
- css
- javascript
- jquery
Hallo,
ich möchte aus einer Liste, die nach einer Texteingabe unter der Textbox angezeigt werden mit dem Key-Up und Key-Down Werte auswählen. Dabei gibts 1 bis 2 Probleme
Ich habe dazu eine Klasse ".Selected" erstellt, wo ich den Hintergrund-Farbwert festlege. Als Test, habe ich einem Punkt in der Liste die Klasse zugeordnet. Leider wird die Farbveränderung aber nicht angezeigt.
<div id="targetDiv">
<input id="StartStation" autocomplete="off" class="valid" type="text">
<input id="SelectedStationStart" name="SelectedStationStart" type="hidden">
<ul id="targetUI">
<li id="359" onclick="javascript:setAutoComplete(359, 'Achim','StartStation','SelectedStationStart')">Achim</li>
**<li id="419" class=".selected" onclick="javascript:setAutoComplete(419, 'Achmer','StartStation','SelectedStationStart')">Achmer</li>**
…
Sicher hab ich irgendwo einen Fehler reingebastelt...
wäre schön wenn mir jemand was sagen könnte.
lg apfelsine
Warum muss ich zur Korrektur eines Links jetzt extra nochmal einen Beitrag schreiben … eine Restriktion die mich hinreichend nervt.
Es fehlte im JSFiddle die Jquery Bibliothek, hab sie angefügt:
https://jsfiddle.net/nz42k12s/17/?utm_source=website&utm_medium=embed&utm_campaign=nz42k12s
Hallo,
Warum muss ich zur Korrektur eines Links jetzt extra nochmal einen Beitrag schreiben
weil das Zeitfenster für Korrekturen schon geschlossen war.
… eine Restriktion die mich hinreichend nervt.
beliebig lange Korrigieren zu dürfen kann aber noch mehr nerven.
Gruß
Jürgen
Hallo JürgenB,
weiß nicht - solange keiner geantwortet hat sollte man seine eigenen Fragen schon bearbeiten dürfen; das Zeitfenster finde ich auch merkwürdig.
Rolf
Hallo Rolf,
weiß nicht - solange keiner geantwortet hat sollte man seine eigenen Fragen schon bearbeiten dürfen; das Zeitfenster finde ich auch merkwürdig.
nicht geantwortet oder nicht gelesen?
Über die maximale Dauer der Bearbeitungsmöglichkeit kann man mMn diskutieren, aber eine Maximalzeit sollte es schon geben.
Gruß
Jürgen
Es gibt IMHO dafür bessere Lösungen. Wenn es nur darum geht, zu erkennen, das es geändert worden ist, zum Beispiel eine optische Markierung das der Beitrag bearbeitet wurde.
Hallo
Wenn es nur darum geht, zu erkennen, das es geändert worden ist, zum Beispiel eine optische Markierung das der Beitrag bearbeitet wurde.
Die gibt es bereits. Einem Leser zuzumuten, die Versionen eines Postings abzugleichen, um relevante Änderungen an einem Posting nachzuvollziehen, die Auswirkungen auf eine oder mehrere Antworten haben, ist noch unzumutbarer.
Tschö, Auge
Einem anderen Leser der hinterher kommt, eine Lösung für etwas sucht und dann erst in einem Baum irgendwo einen Hinweis finden muss, das es dazu noch eine andere Änderung gab auch. Wir kommen da einfach nicht auf einen Nenner ... es ist ok. Ich werde es überleben.
Hallo
Warum muss ich zur Korrektur eines Links jetzt extra nochmal einen Beitrag schreiben … eine Restriktion die mich hinreichend nervt.
Weil es hier, wie wohl in sehr vielen Systemen (unter Anderem [1]) ein Zeitlimit für Änderungen am Posting gibt. Ob man dieses Limit anheben oder gar abschaffen sollte, wurde mehrfach diskutiert und bisher abschlägig beschieden.
Tschö, Auge
Zudem kannst du dein Posting nicht mehr bearbeiten, wenn jemand vor dem Ablauf der 15-Minuten-Frist darauf geantwortet hat. ↩︎
Weil es hier, wie wohl in sehr vielen Systemen
Eine neue Mode, die mir einfach nur auf den Zeiger geht. Das es andere kopieren macht es nicht besser.
Hallo
Weil es hier, wie wohl in sehr vielen Systemen
Eine neue Mode, die mir einfach nur auf den Zeiger geht.
Welche neue Mode? Solche Beschränkungen, insbesondere die beschriebene zeitliche Beschränkung, wird in vielen Foren-und Boardsystemen seit Langem eingesetzt. Zugegeben, in früheren Versionen des auf selfhtml.org eingesetzten CForums gab es keine solchen Beschränkungen. Da konnte man sein Posting einfach überhaupt nicht editieren. Nicht existente Features brauchen halt auch keine Beschränkungen.
Das es andere kopieren macht es nicht besser.
Wer hier von wem kopiert, sei mal dahingestellt.
Tschö, Auge
Hallo Auge,
Zudem kannst du dein Posting nicht mehr bearbeiten, wenn jemand vor dem Ablauf der 15-Minuten-Frist darauf geantwortet hat.
Es müssten sogar 20 Minuten sein.
Gruß
Julius
Hallo apfelsine,
Rolf
Aaaah DANKE du bist ein Schatz 😂 Ich verrate jetzt nicht wie lange ich da schon reingestarrt habe...
@@apfelsine
<li id="359" onclick="javascript:setAutoComplete(359, 'Achim','StartStation','SelectedStationStart')">Achim</li>
Was alle Antwortenden bislang übersehen haben (obwohl sie es besser wissen sollten): Das kann nicht funktionieren (im Sinne von: bei allen Nutzern funktionieren). li
ist kein interaktives Element; das ist bei Tastatursteuerung nicht erreichbar, also nicht anclickbar.
Merke: Niemals nicht-interaktive Elemente als Target für click
-Events vorsehen. Für sowas sind button
s zu verwenden.
Aber eigentlich willst du gar kein JavaScript, sondern eine Combobox? So mit list
-Attribut und datalist
-Element?
Oder doch etwas JavaScript: Lea Verous Awesomplete?
Oder willst du ausschließlich deine vorgegebenen Werte als Eingabemöglichkeiten zulassen? Dann willst du select
mit option
s oder eine Gruppe von Radio-Buttons (vorzugsweise letzteres).
LLAP 🖖
Hallo Gunnar,
danke für deine Anregung! Ich werde es ausprobieren.
kurze Antwort: ja, ich will eine Combobox plus. Eine die eine Id zurückliefert und nicht den ausgewählten Text.
Ich hätte gerne - ohne jQuery-: Ein Textfeld, wo man Ortsnamen eingeben kann. Alsdann erscheint eine Dropdownliste aus der man seinen Ortsnamen auswählen kann. Ab einer gewissen Zahl an Buchstaben sende ich eine Anfrage an den Server, der mir eine gefilterte Liste liefert. Die Elemente dieser Liste sollen unter der Textbox dargestellt werden. Das ganze soll gekapselt werden, sodass ich mit relativ wenig Code aus einem normalen Textfeld ein weiteres Dropdownfeld machen kann. Möglicherweise gibt es noch Anforderungen das einzelne Buchstaben fett geschrieben werden und was weiß ich. Ich möchte volle Kontrolle über die Darstellungsweise. Die Id des gewählten Ortsnamen wird in ein verstecktes Feld geschrieben, das später zusammen mit einem Formular für eine andere Anfrage versendet wird.
Im Grunde genommen ist es mir egal, ob es **li ** ist oder ein anderes Element. Ich habe verschiedene Wissenslücken. Deshalb ist mein Anliegen, Stück für Stück vorzugehen. Daher hab ich erstmal mein angestaubtes Wissen verwendet, aus vor der Zeit von jQuery, was mir geläufig ist und wo ich wusste, es funktioniert erstmal irgendwie. Ja, ich bin nicht mehr sehr fit in Javascript und Html. Mein Wissen ist definitiv veraltet und jQuery bereitet mir mit seinen verschachtelten Funktionen mehr Kopfschmerzen als ich Lust habe. jQuery will ich nicht wirklich verwenden, ausser für die kleineren Dinge. Beim letzten Update meines Projektes auf die neue Version von NopCommerce waren einige dieser netten Funktionen nicht mehr lauffähig, warum auch immer. Es ist - aus meiner Perspektive - ausserdem schwer zu lesen und zu durchschauen.
Es ist nicht ganz leicht auf Anhieb zu erkennen, wo ich was ändern muss, damit die Box am Ende so aussieht und sich verhält, wie ich das will, sodass sie sich in den Rest der Seite integriert.
Es ist ein gigantisches Projekt und ich stehe ziemlich alleine da. Von daher bin ich einfach nur froh, wenn diese Box im ersten Schritt erstmal tut was sie soll, und das tut sie auch. Auch wenn ich das Rad dafür eben selber nochmal erfinden muss. Denn eigentlich ist das ja "nur" 1 Werkzeug für viel mehr. Trotzdem habe ich mir die Zeit genommen, weil ich hoffe es besser zu kapseln als es bisher gelöst ist, sodass es eben auch besser zu warten ist. Formschön kann und will ich es jetzt Stück für Stück machen. Deshalb danke für deinen Input! :-)
Es wird auch nicht die einzige Box bleiben die so arbeiten soll, deshalb ist mein Ziel es in eine Klasse auszulagern. Zumindest hab ich gelesen das es das jetzt geben soll. Allerdings funktioniert das nicht so recht.
Ich finde diesen Mix aus HTML, Javascript und C# in einem einzigen Dokument ein bisschen schrecklich. Noch nie war es so leicht, verwirrenden Spagetthicode zu produzieren.
@@apfelsine
Ich hätte gerne - ohne jQuery-: Ein Textfeld, wo man Ortsnamen eingeben kann. Alsdann erscheint eine Dropdownliste aus der man seinen Ortsnamen auswählen kann. Ab einer gewissen Zahl an Buchstaben sende ich eine Anfrage an den Server, der mir eine gefilterte Liste liefert.
Das kann mehrere Sekunden dauern und dürfte für eine Interaktion viel zu langsam sein.
Du müsstest wohl deine Liste mit Ortsnamen zum Client schicken und die Filterund dort vornehmen lassen – mit JavaScript.
LLAP 🖖
Wenn es weniger Einträge wären, ja. So habe ich es vorher gehabt. Für mehrere tausend, warte ich lieber 3 Buchstaben ab und habe dafür eine wesentlich kleinere Liste, die ich dann on the fly weiter filtern kann.
@@apfelsine
… warte ich lieber 3 Buchstaben ab und habe dafür eine wesentlich kleinere Liste, die ich dann on the fly weiter filtern kann.
Ich sehe den Sinn darin nicht. Ein Nutzer wird kaum nach 3 eingetippten Buchstaben anhalten und warten, ob sich da eine Liste öffnet. (Falls er es überhaupt weiß, dass sich da was tut.) In der Zeit, bis die Liste kommt, hat er den Ortsnamen längst zuende getippt.
LLAP 🖖
@@apfelsine
<li id="359" onclick="javascript:setAutoComplete(359, 'Achim','StartStation','SelectedStationStart')">Achim</li>
Was alle Antwortenden bislang übersehen haben (obwohl sie es besser wissen sollten): Das kann nicht funktionieren (im Sinne von: bei allen Nutzern funktionieren).
li
ist kein interaktives Element; das ist bei Tastatursteuerung nicht erreichbar, also nicht anclickbar.Merke: Niemals nicht-interaktive Elemente als Target für
click
-Events vorsehen. Für sowas sindbutton
s zu verwenden.
ok. Nun habe ich mal nachgeschaut was es denn sonst noch so gibt außer Buttons.
https://wiki.selfhtml.org/wiki/HTML/Kategorien_von_Elementen#Interaktive_Elemente
Irgendwo habe ich gelesen A wäre nicht gut für onclick. Aber mal so einfach gefragt, kann man nicht auch ein Label verwenden? Meinetwegen nehme ich auch einen Button. Ich will es einfach nur wissen.
Ich habe es jetzt mal so probiert(siehe unten), aber das Event anhängen funktioniert nicht, weil das dynamische Element, das ich dem DIV "targetUI" hinzufüge nicht gefunden wird und dadurch das Objekt "myelement" leer ist:
var div = document.getElementById('targetUI');
newelement = '<li class="selected"> <label name="' + item[0] + '" id="' + item[0] + '">' + item[1] + '</label></li>';
if (div != null) {
div.innerHTML += newelement;
var elementname = '\"' + item[0] + '\"';
var myelement= div.getElementsByTagName(elementname);
if (myelement!= null) {
AddEvent(myelement, 'click', function () {
setAutoComplete(item[0], '\'' + item[1] + '\'', '\'' + parent + '\'', '\'' + idField + '\'') });
}
}
Hey
Für sowas sind
button
s zu verwenden.Nun habe ich mal nachgeschaut was es denn sonst noch so gibt außer Buttons.
Einiges. Aber für sowas sind button
s zu verwenden.
Irgendwo habe ich gelesen A wäre nicht gut für onclick.
Ein a
Element verwendet man um einen Link zu setzen. Das funktioniert ohne Zutun eines Skripts. Wenn du keinen Link setzen möchtest, ist a
nicht das richtige Element.
Aber mal so einfach gefragt, kann man nicht auch ein Label verwenden?
Ein label
wird verwendet um ein anderes Element zu beschriften. Ein label
das kein anderes Element beschriftet ist nicht besonders sinnvoll.
Meinetwegen nehme ich auch einen Button.
Nimm einen Button.
var div = document.getElementById('targetUI'); newelement = '<li class="selected"> <label name="' + item[0] + '" id="' + item[0] + '">' + item[1] + '</label></li>';
Zu label
siehe oben. Warum steht da div
als Variablenbezeichner?
if (div != null) { div.innerHTML += newelement;
Das li
steht für list item. Wenn es sich bei div
nicht um eine ol
oder ul
handelt, dann ist li
hier falsch, denn Listenelemente müssen Kindelemente einer Liste sein.
Abgesehen davon produzierst du mit newelement
eine globale Variable. Das möchtest du wahrscheinlich vermeiden und entweder hinter die Deklaration von div
ein Komma setzen, oder die nächste Zeile mit einem var
anfangen.
Wenn du einen Transpiler wie Babel benutzt, kannst du übrigens auf die Stringkonkatenation mit dem überladenen Plusoperator verzichten und statt dessen Templateliterale verwenden. Zu dem Thema habe ich erst kürzlich was geschrieben.
var elementname = '\"' + item[0] + '\"'; var myelement= div.getElementsByTagName(elementname);
Was bezweckst du hiermit? Der erste Index des Arrays item
soll die ID eines Elementes enthalten, nehme ich an, jedenfalls weist du den Wert oben den Attributen
name
und id
zu.
Damit rufst du dann die Methode getElementsByTagName
auf. Das ergibt keinen Sinn. Wie der Name der Methode bereits vermuten lässt, sucht sie Elemente mit einem bestimmten tagName
.
Der Name eines Tags wäre zum Beispiel button
für ein Button-Element. Wenn du das Element nicht über sein Tag sondern über seine ID referenzieren willst, ist getElementsByTagName
nicht die richtige Methode. Dafür gibt es getElementById
oder auch querySelector
.
Davon abgesehen, warum baust du double quotes in den String ein? Innerhalb des als Argument übergebenen Strings sollten keine Anführungszeichen stehen, sonst wirst du nicht finden wonach du suchst.
Davon abgesehen, warum escapest du die doppelten Anführungszeichen, wenn du für das Stringliteral ohnehin einfache verwendest?
Sofern das so vom Browser verarbeitet werden soll, kann das nicht wie gewünscht funktionieren.
if (myelement!= null) {
Die bedingte Anweisung kannst du dir sparen, denn die Bedingung ist hier immer wahr.
Die Methode getElementsByTagName
gibt grundsätzlich eine HTMLCollection zurück. Wenn keine Elemente mit dem gesuchten Tag gefunden wurden, dann eben eine leere Liste, und die ist immer ungleich null
und ungleich undefined
.
AddEvent(myelement, 'click', function () { setAutoComplete(item[0], '\'' + item[1] + '\'', '\'' + parent + '\'', '\'' + idField + '\'') }); } }
Die Funktion AddEvent
ist irgendwo definiert, nehme ich an.
Sie wird aber vermutlich als erstes Argument keine HTMLCollection erwarten, sondern ein Objekt, das die Schnittstelle EventTarget
implementiert, also zum Beispiel ein Element.
Du musst übrigens nicht innerHTML
verwenden, um neue Elemente zu erzeugen. Du kannst auch mit document.createElement('button')
einen Button erzeugen, dem die gewünschten Eigenschaften hinzufügen und ihn dann beispielsweise mit appendChild
einem anderen Element als Kind zuweisen.
const button = document.createElement('button');
button.type = 'button';
button.textContent = item[1];
button.id = item[0];
//...
div.appendChild(button);
button.addEventListener('click', /* ... */);
Hierbei könntest du dir dann die Referenzierung aus dem DOM sparen, denn eine Referenz bekommst du ja schon von createElement
zurück.
Viele Grüße,
Orlok
Hi,
var div = document.getElementById('targetUI'); newelement = '<li class="selected"> <label name="' + item[0] + '" id="' + item[0] + '">' + item[1] + '</label></li>';
Zu
label
siehe oben. Warum steht dadiv
als Variablenbezeichner?
weil das Ding, in dem sich die Liste befindet, die auch ein <ul> hat, (das du hier nicht sehen kannst, weil ich den Code weggelassen habe) ein Div ist.
Abgesehen davon produzierst du mit
newelement
eine globale Variable. Das möchtest du wahrscheinlich vermeiden und entweder hinter die Deklaration vondiv
ein Komma setzen, oder die nächste Zeile mit einemvar
anfangen.
Richtig, habs schlichtweg vergessen zu hinzuschreiben. Danke.
Wenn du einen Transpiler wie Babel benutzt, kannst du übrigens auf die Stringkonkatenation mit dem überladenen Plusoperator verzichten und statt dessen Templateliterale verwenden. Zu dem Thema habe ich erst kürzlich was geschrieben.
var elementname = '\"' + item[0] + '\"'; var myelement= div.getElementsByTagName(elementname);
Was bezweckst du hiermit? Der erste Index des Arrays
item
soll die ID eines Elementes enthalten, nehme ich an, jedenfalls weist du den Wert oben den Attributenname
undid
zu.
ja, nachdem id und document.getElementyById(elementname) nicht funktioniert hat, hab ich ein div.getElementsByTagName(elementname) draus gemacht.
Damit rufst du dann die Methode
getElementsByTagName
auf. Das ergibt keinen Sinn. Wie der Name der Methode bereits vermuten lässt, sucht sie Elemente mit einem bestimmtentagName
.
Jo, das macht in der Tat keinen Sinn. Wie gesagt getElementById hat nicht funktioniert. Ich hab auch das "Tag" von dem Teil überlesen. Es sollte eigentlich getElementsByName heißen.
Davon abgesehen, warum baust du double quotes in den String ein? Innerhalb des als Argument übergebenen Strings sollten keine Anführungszeichen stehen, sonst wirst du nicht finden wonach du suchst.
Reine Unsicherheit ... der Inhalt ist eine Zahl, ich war mir nicht sicher, was der Javascript Compiler daraus macht. Manchmal eben genau das. Eine Zahl, wo es ein String sein soll.
Sofern das so vom Browser verarbeitet werden soll, kann das nicht wie gewünscht funktionieren.
if (myelement!= null) {
Die bedingte Anweisung kannst du dir sparen, denn die Bedingung ist hier immer wahr.
In diesem Fall mag das sein, aber das war nicht immer so. Sie ist ein Überbleibsel als da noch GetElementById stand, damit wurde kein Element zurückgegeben. myelement war mit GetElementById null.
AddEvent(myelement, 'click', function () { setAutoComplete(item[0], '\'' + item[1] + '\'', '\'' + parent + '\'', '\'' + idField + '\'') }); } }
Die Funktion
AddEvent
ist irgendwo definiert, nehme ich an.Sie wird aber vermutlich als erstes Argument keine HTMLCollection erwarten, sondern ein Objekt, das die Schnittstelle
EventTarget
implementiert, also zum Beispiel ein Element.Du musst übrigens nicht
innerHTML
verwenden, um neue Elemente zu erzeugen. Du kannst auch mitdocument.createElement('button')
einen Button erzeugen, dem die gewünschten Eigenschaften hinzufügen und ihn dann beispielsweise mitappendChild
einem anderen Element als Kind zuweisen.const button = document.createElement('button'); button.type = 'button'; button.textContent = item[1]; button.id = item[0]; //... div.appendChild(button); button.addEventListener('click', /* ... */);
Ja, das ist schön, aber als Konstante für eine Schleife? Hattest du einen besonderen Anlass, die Elemente hier als Konstante zu deklarieren?
viele Grüße apfelsine
Hey
Zu
label
siehe oben. Warum steht dadiv
als Variablenbezeichner?weil das Ding, in dem sich die Liste befindet, die auch ein <ul> hat, (das du hier nicht sehen kannst, weil ich den Code weggelassen habe) ein Div ist.
Das heißt, ul
ist ein Kindelement des referenzierten div
s. Oder heißt es, dass in der Variable mit dem Bezeichner div
eigentlich eine ul
drin ist?
Sofern der Wert der Variable div
eine Referenz auf ein gleichnamiges Element ist, führt deine Zuweisung mittels innerHTML
dazu, dass li
nach dem ul
als Kind des div
Elements eingefügt wird, und nicht als Kind der Liste. Das wäre ein Fehler.
Wenn die Variable tatsächlich eine Referenz auf die ul
enthält und nicht auf das div
, solltest du wohl besser einen anderen Bezeichner für die Variable verwenden. ;-)
nachdem id und document.getElementyById(elementname) nicht funktioniert hat, hab ich ein div.getElementsByTagName(elementname) draus gemacht.
Was hast du dir davon versprochen?
Wie gesagt getElementById hat nicht funktioniert.
Dann hätte ich erstmal versucht herauszufinden, warum getElementById
nicht funktioniert. Zum Beispiel hätte ich mit einem console.log(elementname)
eine Kontrollausgabe gemacht und in der Konsole des Browsers nachgesehen, was tatsächlich in der Variable drin ist.
Hättest du das gemacht, wäre dir aufgefallen, dass da was nicht stimmt.
Ich hab auch das "Tag" von dem Teil überlesen. Es sollte eigentlich getElementsByName heißen.
Das hätte es nicht besser gemacht. Abgesehen vom bereits gesagten, hätte ein solcher Aufruf dein Script mit einem Typfehler terminiert. Die Methode getElementsByName
wird nämlich nicht von Elementen implementiert, sondern nur vom Dokumentobjekt.
Davon abgesehen, warum baust du double quotes in den String ein? Innerhalb des als Argument übergebenen Strings sollten keine Anführungszeichen stehen, sonst wirst du nicht finden wonach du suchst.
Reine Unsicherheit ... der Inhalt ist eine Zahl, ich war mir nicht sicher, was der Javascript Compiler daraus macht. Manchmal eben genau das. Eine Zahl, wo es ein String sein soll.
Die Methode getElementById
erwartet tatsächlich einen String, aber du kannst auch eine Zahl übergeben. Dann wird implizit die auf Number.prototype
definierte Methode toString
aufgerufen und die Zahl in eine Zeichenkette umgewandelt.
Du kannst aber auch explizit casten indem du String
als Funktion aufrufst.
Wenn du für die Konkatenation von Strings den Plusoperator verwendest, dann werden Ausdrücke die zu einem anderen Datentyp aufgelöst werden grundsätzlich in Strings umgewandelt. Einzige Ausnahme von der Regel sind Symbole, in anderen Sprachen auch als Atoms bekannt.
if (myelement!= null) {
Die bedingte Anweisung kannst du dir sparen, denn die Bedingung ist hier immer wahr.
In diesem Fall mag das sein, aber das war nicht immer so. Sie ist ein Überbleibsel als da noch GetElementById stand, damit wurde kein Element zurückgegeben. myelement war mit GetElementById null.
Beachten: getElementById
mit kleinem g. ;-) Ja, der Rückgabewert dieser Methode ist nullable. Es wäre allerdings sehr von Vorteil, wenn du das nächste Mal, wenn du hier Code postest, vorher nochmal drübergehst. Denn sonst beschäftigen wir uns hier gegebenenfalls mit Fehlern die eigentlich gar keine sind.
const button = document.createElement('button');
Ja, das ist schön, aber als Konstante für eine Schleife? Hattest du einen besonderen Anlass, die Elemente hier als Konstante zu deklarieren?
Wenn man modernes JavaScript schreibt, ist es üblich const
zu verwenden, wenn die Bindung zwischen Bezeichner und Wert für die Lebenszeit der Variable konstant bleiben soll, also nicht beabsichtigt ist, der Variable später einen anderen Wert zuzuweisen.
Für var
gibt es heute eigentlich kaum noch Anwendungsfälle. Wenn eine Konstante aus oben genanntem Grund nicht das Richtige ist, dann würde man die Variable mit let
deklarieren.
Variablen die mit var
deklariert werden sind immer funktionsweit sichtbar. const
und let
hingegen besitzen Blockscope. Da es gute Praxis ist, Variablen so lokal wie möglich zu deklarieren, sind const
und let
hier klar im Vorteil.
for (var index = 0; index < 5; index++) {
var number = 42;
}
console.log(index, number); // 5, 42
Wenn du wie hier var
verwendest, dann existieren die im Schleifenkopf und Scheifenkörper deklarierten Variablen nach der Abarbeitung der Schleife weiter.
for (let index = 0; index < 5; index++) {
const number = 42;
}
console.log(index, number); // Reference Error
Hier sind die Variable index
und die Konstante number
an die lexikalische Umgebung des Anweisungsblocks der Schleife gebunden. Der Versuch des Zugriffs außerhalb der Schleife erzeugt eine Ausnahme. Die Konstante number
wird hier in jeder Iteration neu initialisiert.
In anderen Sprachen ist es üblich, dass Konstanten etwa nur global deklariert werden können, oder dass ihnen lediglich zur Compilezeit bekannte Werte zugewiesen werden dürfen. Das ist in JavaScript nicht der Fall.
Einer mit const
deklarierten Konstante muss bei ihrer Deklaration ein Wert zugewiesen werden, aber konstant ist hier tatsächlich nur die Bindung von Name und Wert. Wenn statt einem skalaren Wert ein Objekt zugewiesen wird, also mithin auch eine Datenstruktur wie ein Array, kann dieses Objekt nach wie vor verändert werden.
Soll auch ein referenziertes Objekt immutable, also unveränderlich gemacht werden, dann müsste dies explizit angefordert werden, etwa mit der Methode freeze
des Konstruktors Object
.
Die Deklaration mittels var
besitzt einige Quirks, die unter Umständen zu schwer auffindbaren Fehlern im Programm führen können. So sind zum Beispiel Mehrfachdeklarationen in der selben lexikalischen Umgebung mit var
ohne weiteres möglich. Ebenso die Referenzierung der Variable vor ihrer Deklaration.
Solche potentiell fehlerträchtigen Praktiken sind bei der Verwendung von const
und let
von vorneherein ausgeschlossen, weswegen man grundsätzlich diese Keywords für die Deklaration von Variablen und Konstanten verwenden sollte.
Sowohl const
als auch let
werden mittlerweile von den allermeisten Browsern unterstützt. Allerdings kann es nicht schaden, sich vor der Verwendung im Produktiveinsatz nochmal zu informieren, ob alle Browser die man berücksichtigen will, oder muss, die Syntax beherrschen.
Aber das gilt natürlch nur, sofern man den Code vor der Auslieferung nicht ohnehin in ein kompatibles Format übersetzen lässt.
Viele Grüße,
Orlok
Hallo nochmal,
also ich habe jetzt einige Änderungen vorgenommen und mein Event wird immer überschrieben, es übergibt nur einen Wert an setAutoComplete. Nämlich den letzten aus der Iteration.
$.each(msg, function (i, value) {
item = value.split(";");
try{
var div = document.getElementById('targetUI');
//create new listelement for station and mark as selected
var li = document.createElement('li');
li.type = 'li';
li.className = 'selected';
//create button in listelement for station
var button = document.createElement('button');
button.type = 'button';
button.textContent = item[1];
button.id = item[0];
if (i == 0) {
button.className = 'dropdownelement';
}
//add button to listelement
li.appendChild(button);
if (div != null) {
//append to list-node previously added before iteration
div.appendChild(li);
//AddEvent(button, 'click', function () { setAutoComplete(item[0], item[1], parent, idField) });
button.addEventListener('click', function () { setAutoComplete(item[0], item[1], parent, idField) });
//button.addEventListener('click', function () { setAutoComplete(fcn) });
}
}
catch(e)
{
alert("Fehler: " + e.message);
}
}); // end each iteration
Hey
also ich habe jetzt einige Änderungen vorgenommen und mein Event wird immer überschrieben, es übergibt nur einen Wert an setAutoComplete. Nämlich den letzten aus der Iteration.
Und das wundert dich? ;-)
$.each(msg, function (i, value) {
Zunächst mal wäre zu erwähnen, dass es zwar grundsätzlich sinnvoll ist, wenn man schon ein Framework wie jQuery einbindet, sich auch dessen Methoden zu bedienen, aber von jeder Regel gibt es Ausnahmen.
Hier die generische Methode each
des jQuery-Objektes zu verwenden ist nicht falsch, aber auch nicht wirklich sinnvoll. Zumindest, sofern es sich bei dem Wert von msg
um ein iterierbares Objekt handelt, also zum Beispiel um ein gewöhnliches Array.
Sollte dies der Fall sein, wäre die native Methode forEach
vorzuziehen, welche direkt auf jeder Arrayinstanz aufgerufen werden kann.
msg.forEach(function (value, index /* array */) {
// do something
});
Beachte, dass hier die Reihenfolge der Argumente, mit denen die Callback-Funktion aufgerufen wird anders herum ist als bei der Methode each
, also erst der aktuelle Wert und dann der Index.
Als drittes Argument wird darüber hinaus noch eine Referenz auf das Array übergeben, über welches iteriert wird. Aber die brauchst du hier nicht, also kann man sich die Deklaration eines dritten Parameters in diesem Fall sparen.
item = value.split(";");
An dieser Stelle liegt die Ursache für das von dir beschriebene Problem.
button.addEventListener('click', function () { setAutoComplete(item[0], item[1], parent, idField) });
Was glaubst du passiert, wenn diese Funktion aufgerufen wird?
In diesem Fall wird versucht, die Referenz mit dem Bezeichner item
aufzulösen, was innerhalb dieser Funktion nicht möglich ist, da weder ein Parameter noch eine lokale Variable mit diesem Bezeichner deklariert wurde.
Da du in der äußeren lexikalischen Umgebung der Handlerfunktion, also der an each
übergebenen Rückruffunktion, wie gesehen ebenfalls keine Variable mit dem Bezeichner item
deklariert hast, sondern lediglich eine Variable in einem umgebenden Gültigkeitsbereich referenzierst, wird entsprechend dort weitergesucht.
Schließlich wird von jeder Handlerfunktion, die du innerhalb der an each
übergebenen Callback-Funktion definierst, ein und dieselbe Variable referenziert, und da die Eventhandler erst ausgeführt werden, wenn die Iteration abgeschlossen ist, wird entsprechend der Wert zurückgegeben, welcher der Variable item
zuletzt zugewiesen wurde.
var item = value.split(';');
Was du also eigentlich möchtest, ist bei jedem Aufruf der an each
übergebenen Funktion eine lokale Variable anzulegen, sodass der Wert in der Closure, also dem Funktionsabschluss konserviert wird.
try{ //.. } catch(e) { alert("Fehler: " + e.message); }
Ich würde mich für einen Klammerstil entscheiden. Es fördert nicht unbedingt die Lesbarkeit des Codes, wenn man das mal so und mal so macht. Davon abgesehen scheint mir try
und catch
an dieser Stelle ziemlich überflüssig.
Hier kann eigentlich nur dann ein Fehler auftreten, wenn in der Variable div
kein Objekt ist, dass die Methode appendChild
implementiert, und hier prüfst du immerhin gegen null
und undefined
.
Sollte das Statement und der Aufruf von alert
dem Debugging dienen, dann möchte ich noch hinzufügen, dass dies nicht der richtige Ansatz ist. Hierfür solltest du die Entwicklerwerkzeuge deines Browsers verwenden. Dort werden dir in der Konsole Fehler angezeigt, üblicherweise mit Typ, Message, Stacktrace, Script und Zeilennummer.
Über die console
API stehen dir verschiedene Methoden zur Verfügung, um Kontrollausgaben in der Konsole des Browsers zu machen. Unter anderem log
für einfache Kontrollausgaben, dir
für Inspektionen, warn
für Warnungen, error
für eigene Fehlermeldungen, info
für zusätzliche Informationen und assert
für Fehlermeldungen, wenn eine bestimmte Annahme nicht zutrifft.
const foo = null;
console.assert(foo === 'bar', 'foo is not bar');
Das ergibt einen Fehler, Assertion failed. Darüber hinaus kannst du im Script mit dem debugger
-Statement auch Breakpoints setzen, an denen dein Programm angehalten werden soll. Dann kannst du die verschiedenen Variablen und die Werte die zu einem bestimmten Zeitpunkt mit ihnen verknüpft sind im Debugger des Browsers inspizieren.
var div = document.getElementById('targetUI'); if (div != null) { div.appendChild(li);
Das hatte ich in meiner anderen Antwort ja schon angesprochen. Es ist extrem verwirrend, wenn du eine Variable div
nennst, die eigentlich eine ul
referenziert. Das ist nicht nachvollziehbar und sollte geändert werden. Davon abgesehen verwendest du jQuery, also kannst du dir den Aufruf der Methode getElementById
hier sparen und einfach $('#targetUI')
schreiben.
const $targetUI = $('#targetUI');
if ($targetUI.length) {
Wenn es kein Element mit der ID targetUI
gibt, wird von jQuery ein leeres Objekt zurückgegeben. Du kannst also wie in dem Beispiel oben auf die Existenz prüfen. Ich habe hier dem Namen der Konstante das Prefix $
vorangestellt, um zu verdeutlichen, dass hier ein jQuery-Objekt und nicht direkt ein Element referenziert wird, aber das kannst du natürlich halten wie du willst.
//create new listelement for station and mark as selected var li = document.createElement('li'); li.type = 'li'; li.className = 'selected'; //create button in listelement for station var button = document.createElement('button'); button.type = 'button'; button.textContent = item[1]; button.id = item[0]; if (i == 0) { button.className = 'dropdownelement'; } //add button to listelement li.appendChild(button);
Auch hierfür wären entsprechende jQuery-Methoden zu verwenden, wenn du dieses Framework schon einbindest. Allerdings ist nicht alles was du hier tust sinnvoll.
li.type = 'li';
Das ergibt zum Beispiel keinen Sinn, denn li
hat kein type
-Attribut.
Darüber hinaus solltest du beachten, dass die Vergabe einer Klasse selected
nicht dazu führt, dass Benutzer assistiver Software über den Zustand deines Widgets in Kenntnis gesetzt werden. Hier wirst also noch etwas Arbeit investieren müssen, wenn das Ganze zugänglich sein soll.
Viele Grüße,
Orlok
Hi
also ich habe jetzt einige Änderungen vorgenommen und mein Event wird immer überschrieben, es übergibt nur einen Wert an setAutoComplete. Nämlich den letzten aus der Iteration.
Und das wundert dich? ;-)
Nein nicht wirklich. Was man halt so tut, wenn man sich durchhangelt.
Als drittes Argument wird darüber hinaus noch eine Referenz auf das Array übergeben, über welches iteriert wird. Aber die brauchst du hier nicht, also kann man sich die Deklaration eines dritten Parameters in diesem Fall sparen.
item = value.split(";");
An dieser Stelle liegt die Ursache für das von dir beschriebene Problem.
button.addEventListener('click', function () { setAutoComplete(item[0], item[1], parent, idField) });
Was glaubst du passiert, wenn diese Funktion aufgerufen wird?
Meine Funktion tanzt Samba?...
Schließlich wird von jeder Handlerfunktion, die du innerhalb der an
each
übergebenen Callback-Funktion definierst, ein und dieselbe Variable referenziert, und da die Eventhandler erst ausgeführt werden, wenn die Iteration abgeschlossen ist, wird entsprechend der Wert zurückgegeben, welcher der Variableitem
zuletzt zugewiesen wurde.
Scherz beiseite, tja, das macht Sinn. Das führt mich dann wieder zu meiner Stringlösung zurück…
var item = value.split(';');
Was du also eigentlich möchtest, ist bei jedem Aufruf der an
each
übergebenen Funktion eine lokale Variable anzulegen, sodass der Wert in der Closure, also dem Funktionsabschluss konserviert wird.
und das soll dann funktionieren...? ich werde es ausprobieren.
Ich würde mich für einen Klammerstil entscheiden. Es fördert nicht unbedingt die Lesbarkeit des Codes, wenn man das mal so und mal so macht. Davon abgesehen scheint mir
try
undcatch
an dieser Stelle ziemlich überflüssig.
Mag sein, aber nicht, wenn ich ständig dran rumbastel. Ich finde die Browserkonsole auch nicht sehr benutzerfreundlich und ehrlichgesagt in Teilen ziemlich umständlich in der Handhabung sodass ich manchmal am liebsten in den Bildschirm greifen würde. Für einfache Sachen reicht mir das Alert.
Darüber hinaus kannst du im Script mit dem
debugger
-Statement auch Breakpoints setzen, an denen dein Programm angehalten werden soll.
…
Dann kannst du die verschiedenen Variablen und die Werte die zu einem bestimmten Zeitpunkt mit ihnen verknüpft sind im Debugger des Browsers inspizieren.
Das ist mir bekannt.
var div = document.getElementById('targetUI'); if (div != null) { div.appendChild(li);
Das hatte ich in meiner anderen Antwort ja schon angesprochen. Es ist extrem verwirrend, wenn du eine Variable
div
nennst, die eigentlich eineul
referenziert. Das ist nicht nachvollziehbar und sollte geändert werden.
Nein, weil du den Code vorher eben nicht kennst. Und targetUI IST ein DIV, an das ein Kindelement UL angehängt wird. Dieser Abschnitt wäre normalerweise ausserhalb der Schleife. Der muss nur einmal ausgeführt werden. Ich habe auch keine Lust über Sinnhaftigkeiten von Variablennamen zu diskutieren, wenn sie nicht gerade ein geschützter Begriff sind und damit die Funktionsweise beeinträchtigen würden.
//create new listelement for station and mark as selected var li = document.createElement('li'); li.type = 'li'; if (i == 0) { li.className = 'selected'; } //create button in listelement for station var button = document.createElement('button'); button.type = 'button'; button.textContent = item[1]; button.id = item[0]; //if (i == 0) { //falscher Platz button.className = 'dropdownelement'; //} //add button to listelement li.appendChild(button);
Auch hierfür wären entsprechende jQuery-Methoden zu verwenden, wenn du dieses Framework schon einbindest. Allerdings ist nicht alles was du hier tust sinnvoll.
Das kannst du aus meiner Perspektive nicht beurteilen. Du siehst nur einen Ausschnitt von einem Prozess. Die Historie, warum was wie so steht wie es ist, mag für mich sinnvoll gewesen sein zu diesem Zeitpunkt. Und wie ich bereits erwähnt habe, möchte ich jQuery so wenig wie möglich verwenden. Es ist drin, ob ich es nutze oder nicht. Ich habe keinen Bock mehr das es mir beim nächsten Update wieder was zerschießt. Kann ich zwar trotzdem nicht ausschließen aber na ja.
li.type = 'li';
Das ergibt zum Beispiel keinen Sinn, denn
li
hat keintype
-Attribut.Darüber hinaus solltest du beachten, dass die Vergabe einer Klasse
selected
nicht dazu führt, dass Benutzer assistiver Software
Assistiver Software??? Meinst du einen Debugger??
über den Zustand deines Widgets in Kenntnis gesetzt werden. Hier wirst also noch etwas Arbeit investieren müssen, wenn das Ganze zugänglich sein soll.
nun - ohne die Dokumentation zu wälzen habe ich einfach mal angenommen, das ich meinen Stylesheet "selected" da in class unterbringen kann. Mir ist gestern dazu nichts falsches beim testen aufgefallen. Allerdings habe ich das If an einen sinnfreien Ort gesetzt, es sollte eigentlich bei "selected" sein. Der Type bei li macht in der Tat keinen Sinn.
Danke für die Unterstützung!
apfelsine
Hallo apfelsine,
Darüber hinaus solltest du beachten, dass die Vergabe einer Klasse
selected
nicht dazu führt, dass Benutzer assistiver SoftwareAssistiver Software??? Meinst du einen Debugger??
Nein. Beispielsweise ein Screenreader.
über den Zustand deines Widgets in Kenntnis gesetzt werden. Hier wirst also noch etwas Arbeit investieren müssen, wenn das Ganze zugänglich sein soll.
nun - ohne die Dokumentation zu wälzen habe ich einfach mal angenommen, das ich meinen Stylesheet "selected" da in class unterbringen kann. Mir ist gestern dazu nichts falsches beim testen aufgefallen.
Hast du auch ausprobiert, ob du das nur mit der Tastatur – also komplett ohne Maus – bedienen kannst? Funktioniert es auch auf Touchscreens?
Gruß
Julius
Der Fehler der da ein Fehler gewesen sein sollte, ist kein Fehler mehr,
wenn auch viele andere Dinge ein Fehler gewesen sein mögen.
@@apfelsine
Irgendwo habe ich gelesen A wäre nicht gut für onclick.
Das kann man so pauschal nicht sagen. Evangelina Ferreira gibt in ihrem Artikel 10 guidelines to improve your web accessibility, Abschnitt 6. Use the right mark-up unter „Button vs. <a> tag“ gute Hinweise, wann Buttons und wann Links zu verwenden sind.
Meist will man mit click
-Events Aktionen auf einer Seite auslösen; also Buttons verwenden.
Im nachfolgenden Abschnitt 7. Use roles when necessary gibt es aber ein Beispiel, wo ein click
-Event für ein a
-Element sinnvoll ist – mit role="button"
. Siehe dazu auch meinen Kommentar.
LLAP 🖖
Nur mal so nebenbei bemerkt und zusätzlich zu dem, was Gunnar bereits sagte …
<li onclick="javascript:setAutoComplete()">
Du könntest hier auch folgendes schreiben:
<li onclick="php:setAutoComplete()">
Das funktioniert. Magic!
Jedenfalls habe ich den Verdacht, dass du nicht wirklich weißt, was du hier tust, denn anders kann ich mir die Verwendung dieser Syntax an dieser Stelle nicht erklären.
Was du hier verwendest ist ein sogenanntes Labelled Statement, welches sich zusammensetzt aus einem Bezeichner für das Label, einem Doppelpunkt sowie einem weiteren Statement. Dieses Konstrukt wird selten verwendet und wenn, dann meist im Zusammenhang mit verschachtelten Schleifen.
outerLoop:
for (let i = 0; i < 5; i++) {
innerLoop:
for (let j = 0; j < 5; j++) {
if (condition) {
continue outerLoop;
}
}
}
Indem man den Schleifen durch das Label einen Namen zuweist, kann man mit den Statements break
und continue
steuern, welche Schleife abgebrochen oder fortgesetzt werden soll.
Die einzige weitere halbwegs sinnvolle Anwendung für ein Label ist in Verbindung mit Blöcken für Anweisungen, denen ebenfalls auf diese Art ein Name zugewiesen werden kann. Hier kann die weitere Ausführung eines Anweisungsblocks mit break
unterbunden werden.
outerBlock: {
innerBlock: {
if (condition) {
break outerBlock;
}
}
}
Das führt allerdings zu schlecht lesbarem Code und wird daher fast nie verwendet.
Wie anfangs erwähnt, kann hinter dem Doppelpunkt jedoch irgendein Statement stehen, dass heißt, ein Label kann nicht nur auf die zuvor beschriebenen Arten verwendet werden, sondern zum Beispiel auch zusammen mit einer Call Expression, also einem Funktionsaufruf.
Eben so, wie du das in deinem Beispielcode mit dem Label javascript
machst:
javascript:setAutoComplete()
Das erzeugt aus genanntem Grund keinen Syntaxfehler, ist aber natürlich vollkommen sinnfrei, denn das Label kann von nirgendwo referenziert werden, und selbst wenn, könnte man nichts damit anfangen.
Auch als Annotation wäre es ziemlich überflüssig, denn was für Code sollte an dieser Stelle denn sonst ausgeführt werden?
Naja, genau genommen sollte an dieser Stelle überhaupt kein Code ausgeführt werden.
Eventhandler über Attribute zu registrieren ist ziemlich schlechte Praxis. Markup und Scriptcode sollte nicht miteinander vermischt werden, ebensowenig wie HTML und CSS, denn das führt zu schlecht les- und wartbarem Code.
Registriere deine Eventhandler im Script und verwende hierzu die auf allen Elementen sowie einigen anderen Objekten definierte Methode addEventListener
, oder, wenn du ohnehin jQuery einbindest, die von diesem Framework bereitgestellten Methoden.
document.querySelector('button').addEventListener('click', function (event) {
setAutoComplete(...);
});
Die Methode addEventListener
erwartet als erstes Argument einen String mit dem Typ des Ereignisses, hier also click, ohne das Prefix on, und als zweites Argument die Funktion die beim Eintritt des Ereignisses aufgerufen werden soll.
$('button').click(function (event) {
setAutoComplete(...);
});
jQuery bietet für das Event click
sogar eine eigene Methode an. Es ist also nichtmal nötig die Methode on
zu bemühen.
Solltest du dich für eine JavaScript-Lösung entscheiden, dann hoffentlich auch dafür, interaktive Elemente wie Buttons zu verwenden, damit deine Seite auch von solchen Benutzern verwendet werden kann, die auf eine funktionierende Tastaturbedienung angewiesen sind.
Wenn du das machst, dann brauchst du dich auch nicht um das Abfangen von Tastaturevents wie zum Beispiel keydown
zu kümmern, denn wenn ein interaktives Element durch eine Tastatureingabe aktiviert wird, dann wird auch ein click
Event ausgelöst.
Aber vielleicht brauchst du hier ja auch gar kein JavaScript.
Danke für deinen Input.
Ich habe hier zusammenfassend geantwortet: https://forum.selfhtml.org/self/2017/aug/15/select-li-element-on-keyup/1702098#m1702098