HTML5-Tutorium: JavaScript: Hello World 03: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Keine Bearbeitungszusammenfassung
 
(83 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}
{{HTML5-Tutorium:JavaScript:HelloWorld:Menü}}


'''Musterlösung''': [https://glossar.hs-augsburg.de/beispiel/tutorium/es6/hello_world/WK_HelloWorld03/web/index.html <code>index.html</code>],
'''Musterlösung''': [https://glossar.hs-augsburg.de/beispiel/tutorium/2024/wk_hello_world/wk_hello_world_03/web/index.html <code>index.html</code>]
[https://glossar.hs-augsburg.de/beispiel/tutorium/es6/hello_world/WK_HelloWorld03/web/index2.html <code>index2.html</code>]
([{{Git-Server}}/kowa/wk_hello_world_03.git Git-Repository], [https://validator.nu/?doc=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2024%2Fwk_hello_world%2Fwk_hello_world_03%2Fweb%2Findex.html HTML validate], [https://css-validator.org/validator?uri=https%3A%2F%2Fglossar.hs-augsburg.de%2Fbeispiel%2Ftutorium%2F2024%2Fwk_hello_world%2Fwk_hello_world_03%2Fweb%2Findex.html&profile=css21&usermedium=all&warning=1&lang=de CSS validate])
([https://gitlab.multimedia.hs-augsburg.de:8888/kowa/WK_HelloWorld03 Git-Repository])
 
==Anwendungsfälle (Use Cases)==
==Anwendungsfälle (Use Cases)==
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_02|zweiten Teil des Tutoriums]]
Gegenüber dem [[HTML5-Tutorium:_JavaScript:_Hello_World_02|zweiten Teil des Tutoriums]]
Zeile 9: Zeile 9:
Welt begrüßt werden, sondern der Benutzer, der die Web-Anwendung gestartet hat.
Welt begrüßt werden, sondern der Benutzer, der die Web-Anwendung gestartet hat.
Dazu muss er zunächst nach seinem Namen gefragt werden.
Dazu muss er zunächst nach seinem Namen gefragt werden.
Anschließend wird das HTML-Dokument mit Hilfe von [[JavaScript]] umgestaltet: Das Eingabeformular wird ausgeblendet
Anschließend wird das HTML-Dokumnt mit Hilfe von [[JavaScript]] umgestaltet: Das Eingabeformular wird ausgeblendet
und stattdessen wird die Begrüßungsformel angezeigt.
und stattdessen wird die Begrüßungsformel angezeigt.


==Erstellen eines neuen Projektes==
==Erstellen eines neuen Projekts==
 
Erstellen Sie ein neues Projekt  „<code>HelloWorld03</code>“.
 
Kopieren Sie anschließend die Projektdateien (insbesondere den Ordner „<code>web</code>“ mit den Dateien „<code>index.html</code>“ und „<code>main.css</code>“ sowie die Datei  <code>.gitignore</code>) aus dem zweiten Teil des Tutoriums und passen Sie den Titel in der HTML-Datei an.


Erstellen Sie für Ihr Projekt ein Git-Repository, committen Sie und pushen Sie es dann auf den Git-Server.
Erstellen Sie ein neues Projekt <code>hello_world_03</code> als Fork von <code>hello_world_02</code> (siehe [[HTML5-Tutorium: JavaScript: Hello World 02|Hello-World-02-Tutorium]]).


==Single-Page-Web-Anwendung==
==Single-Page-Web-Anwendung==


Die Anwendung wird als Single-Page-Web-Anwendung (Onepager) realisiert.
Die Anwendung wird als Single-Page-Web-Anwendung (Onepager) realisiert.
Das HTML-Dokument <code>index.html</code>enthält zwei Abschnitte (sections),
Das HTML-Dokument <code>index.html</code> enthält zwei Abschnitte (sections),
eines mit einem Formular zur Eingabe des Namens und ein zweites zur Begrüßung des
eines mit einem Formular zur Eingabe des Namens und ein zweites zur Begrüßung des
Benutzers, nachdem er seinen Namen eingegeben hat.
Benutzers, nachdem er seinen Namen eingegeben hat.


Für jede Seite fügen wir in <code>index.html</code>
Für jeden Abschnitt fügen wir in <code>index.html</code>
einen HTML-Abschnitt „<code>&lt;section id="..."&gt; ...&lt;/section&gt;</code>“
eine HTML-Section „<code>&lt;section id="..."&gt; ...&lt;/section&gt;</code>“
ein. Das Section-Element gruppiert einen logischen Abschnitt oder ein Kapitel  
ein. Das Section-Element gruppiert einen logischen Abschnitt oder ein Kapitel  
eines HTML-Dokuments. Es soll laut [https://www.w3.org/TR/html5/sections.html#the-section-element Spezifikation]
eines HTML-Dokuments. Es soll laut [https://www.w3.org/TR/html5/sections.html#the-section-element Spezifikation]
eine Überschrift enthalten, die das Thema des Abschnitts beschreibt. Man hätte auch ein Div-Element „<code>&lt;div id="..."&gt; ...&lt;/div&gt;</code>“
eine Überschrift enthalten, die das Thema des Abschnitts beschreibt.
 
Man hätte auch ein Div-Element „<code>&lt;div id="..."&gt; ...&lt;/div&gt;</code>“
anstelle des Section-Elements verwenden können. Ein Div-Element hat keinerlei Semantik (Bedeutung), es dient lediglich der Strukturierung einer
anstelle des Section-Elements verwenden können. Ein Div-Element hat keinerlei Semantik (Bedeutung), es dient lediglich der Strukturierung einer
HTML-Datei. Die (spezifikationsgemäße) Verwendung von Section- und Article-Elementen, die in HTML5 eingeführt wurden, hat den Vorteil,
HTML-Datei. Die (spezifikationsgemäße) Verwendung von Section- und Article-Elementen, die in HTML5 eingeführt wurden, hat den Vorteil,
dass Browser deren Bedeutung kennen und daher geeignete Defaultstyles verwenden können, falls der Entwickler keine entsprechenden Styles angibt.
dass Browser deren Bedeutung kennen und daher geeignete Defaultstyles verwenden können, falls der Entwickler keine entsprechenden Styles angibt.
Ein Beispiel sind Browser für Blinde. Derartige Browser lesen die Inhalte entweder vor oder geben sie textuell über eine sogenannte [[Braillezeile]] aus.
Ein Beispiel sind Browser für Blinde. Diese Browser lesen die Inhalte entweder vor oder geben sie textuell über eine sogenannte [[Braillezeile]] aus.
Für derartige Browser werden meist keine CSS-Layout-Vorgaben gemacht (obwohl dies problemlos möglich wäre) und daher ist es wichtig,  
Für derartige Browser werden meist keine CSS-Layout-Vorgaben gemacht (obwohl dies problemlos möglich wäre) und daher ist es wichtig,  
Strukturelementen eine Semantik zuzuordnen. Ein Blindenbrowser könnte beim Vorlesen eines Dokuments beispielsweise stets das Wort „Kapitel“  
Strukturelementen eine Semantik zuzuordnen. Ein Blindenbrowser könnte beim Vorlesen eines Dokuments beispielsweise stets das Wort „Kapitel“  
Zeile 47: Zeile 45:
   <section id="section_hello" class="hidden">
   <section id="section_hello" class="hidden">
     <h1 id="heading_hello">Hello, ...!</h1>
     <h1 id="heading_hello">Hello, ...!</h1>
     <p>Welcome to Multimedia Programming!</p>
     <p>Welcome to Full Stack Web Development!</p>
   </section>
   </section>
</body>
</body>
Zeile 57: Zeile 55:
„<code>id="section_form"</code>“, „<code>id="section_hello"</code>“ und „<code>id="heading_hello"</code>“.
„<code>id="section_form"</code>“, „<code>id="section_hello"</code>“ und „<code>id="heading_hello"</code>“.
Jedes öffnende HTML-Element darf mit einem derartigen Attribut versehen werden.  
Jedes öffnende HTML-Element darf mit einem derartigen Attribut versehen werden.  
'''Allerdings darf es in einer HTML-Datei keine zwei <code>id</code>-Attribute mit demselben Namen geben.'''
'''Beachten Sie, das es in einer HTML-Datei keine zwei <code>id</code>-Attribute mit demselben Namen geben darf.'''


Die Vergabe von <code>id</code>-Attributen bringt zwei Vorteile mit sich: Zum einen können  
Die Vergabe von <code>id</code>-Attributen bringt zwei Vorteile mit sich: Zum einen können  
Zeile 71: Zeile 69:
</source>
</source>


Fügen Sie nun in das öffnende Tag des Section-Elements mit dem Identifikator <code>section_hello</code>
Fügen Sie nun in das öffnende Tag des Section-Elements mit dem Identifikator <code>section_hello</code>
das Attribut-Wert-Paar „<code>class="hidden"</code>“ ein. Ein <code>class</code>-Attribut darf im Gegensatz zu einem  
das Attribut-Wert-Paar „<code>class="hidden"</code>“ ein. Ein <code>class</code>-Attribut darf im Gegensatz zu einem  
<code>id</code>-Attribut beliebig vielen Elementen zugeordnet werden, ohne dass sich der zugehörige Wert unterscheiden muss.
<code>id</code>-Attribut beliebig vielen Elementen zugeordnet werden, ohne dass sich der zugehörige Wert unterscheiden muss.
Zeile 99: Zeile 97:
   <div>
   <div>
     <label for="input_name">What's your name?</label>
     <label for="input_name">What's your name?</label>
     <input id="input_name"/>
     <input id="input_name" autofocus>
   </div>
   </div>
   <div>
   <div>
     <input                   type="reset" value="Reset"/>
     <input id="button_reset"  type="reset" value="Reset">
     <input id="button_submit" type="button" value="Say hello"/>
     <input id="button_submit" type="button" value="Say hello">
   </div>
   </div>
</form>
</form>
Zeile 117: Zeile 115:


Beachten Sie, dass zwei weitere <code>id</code>-Attribute eingeführt wurden: „<code>id="input_name"</code>“ und „<code>id="button_submit"</code>“.
Beachten Sie, dass zwei weitere <code>id</code>-Attribute eingeführt wurden: „<code>id="input_name"</code>“ und „<code>id="button_submit"</code>“.
Diese werden für den Zugriff von JavaScript aus auf das Dokument benötigt. Über den Identifikator <code>input_name</code>kann auf
Diese werden für den Zugriff von JavaScript aus auf das Dokument benötigt. Über den Identifikator <code>input_name</code> kann auf
den Inhalt des Textfeldes, {{dh}} auf den Namen, den der Benutzer eingegeben hat, zugegriffen werden. Außerdem kann  
den Inhalt des Textfeldes, {{dh}} auf den Namen, den der Benutzer eingegeben hat, zugegriffen werden. Außerdem kann  
mit Hilfe dieses Identifikators das Label-Element über das Attribut <code>for</code>an das Textfeld gekoppelt werden.
mit Hilfe dieses Identifikators das Label-Element über das Attribut <code>for</code> an das Textfeld gekoppelt werden.
Damit weiß der Browser, auf welches Input-Element sich das Label-Element bezieht. Auch dies ist wieder besonders wichtig, wenn der
Damit weiß der Browser, auf welches Input-Element sich das Label-Element bezieht. Auch dies ist wieder besonders wichtig, wenn der
Zusammenhang nicht optisch (per CSS) hergestellt werden kann bzw. hergestellt wird. Damit lassen sich aber auch [[Checkbox]]es
Zusammenhang nicht optisch (per CSS) hergestellt werden kann bzw. hergestellt wird. Damit lassen sich aber auch [[Checkbox]]es
Zeile 125: Zeile 123:


Der Identifikator für den Submit-Button wird benötigt, um in JavaScript die Klick-Aktion des Benutzers abfangen zu können.  
Der Identifikator für den Submit-Button wird benötigt, um in JavaScript die Klick-Aktion des Benutzers abfangen zu können.  
Normalerweise im Form-Element in einem Attribut namens <code>action</code>ein URI angegeben. Dieser verweist auf eine  
Normalerweise im Form-Element in einem Attribut namens <code>action</code> ein URI angegeben. Dieser verweist auf eine  
Serveradresse, an den die vom Benutzer erfassten Daten bei einem Klick auf einen echten Submit-Button (<code>type="submit"</code>) übermittelt werden.
Serveradresse, an den die vom Benutzer erfassten Daten bei einem Klick auf einen echten Submit-Button (<code>type="submit"</code>) übermittelt werden.
In dieser Web-Anwendung werden keine Daten an einen Server übermittelt. Alle Benutzereingaben werden direkt
In dieser Web-Anwendung werden keine Daten an einen Server übermittelt. Alle Benutzereingaben werden direkt
im Browser (per JavaScript) verarbeitet. Deshalb wird das Action-Attribut nicht benötigt und als Submit-Button wird ein einfacher Button (<code>type="button"</code>)
im Browser (per JavaScript) verarbeitet. Deshalb wird das Action-Attribut nicht benötigt und als Submit-Button wird ein einfacher Button (<code>type="button"</code>)
eingesetzt.
eingesetzt.
Sie können und sollten das Formular auch noch per CSS stylen:
<source lang="css">
p, label
{ font-family: "Times New Roman", Times, serif;
  font-size:  100% !important;
}
label, input
{ width:      10em;
  font-size:  100%;
  display:    inline-block;
  box-sizing: border-box;
  margin:    0.5ex;
}
label
{ text-align: right; }
#section_form
{ text-align:  center;
  margin-left:  auto;
  margin-right: auto;
}
#section_form > h1
{ margin-bottom: 0.5ex; }
#section_hello > p
{ font-size: 156.25% !important; /* 1.25^2 = 1.5625 */ }
</source>


Wenn alles zu Ihrer Zufriedenheit ausgefallen ist, sollten Sie den aktuellen Stand in Ihr Repository einspielen.
Wenn alles zu Ihrer Zufriedenheit ausgefallen ist, sollten Sie den aktuellen Stand in Ihr Repository einspielen.
Zeile 135: Zeile 165:
==Interaktion mittels JavaScript==
==Interaktion mittels JavaScript==


Erzeugen Sie eine leere JavaScript-Datei:
Öffnen Sie in Ihrem Browser die Musterlösung:
  [https://glossar.hs-augsburg.de/beispiel/tutorium/2024/wk_hello_world/wk_hello_world_03/web/index.html <code>index.html</code>]
 
Öffnen Sie im Browser die Entwicklerumgebung (Rechtsklick auf Web-Seite im Browser und <code>Untersuchen</code> anklicken).


* Rechtsklick auf die Projektwurzel „<code>HelloWorld03</code>“ im Dateibrowser → <code>New</code> → <code>JavaScript file</code>
Öffnen Sie im Browser die Konsole. Falls Sie dazu aufgefordert werden (Firefox) geben Sie <code>'Einfügen erlauben'</code> als Befehl in das Konsofesnter ein.
* Name: „<code>main.js</code>“ → <code>OK</code>


Fügen Sie folgenden JavaScript-Code in diese Datei ein
Abschließend können Sie im Konsolfester verschiedene EcmaScript-Befehler ausprobieren. Kopeiren Sie der Reihe nach die einzelnen Befehle (Zeilen) ins Konsolfenster. Was bewirken sie?
 
<source lang="javascript">
document.getElementById('section_hello').classList.remove('hidden');
document.getElementById('section_hello').classList.add('hidden');
 
document
document.children
document.children[0]
document.children[0].children
document.children[0].children[1]
document.children[0].children[1].children
document.children[0].children[1].children[1]
document.children[0].children[1].children[1].classList
document.children[0].children[1].children[1].classList.remove('hidden')
document.children[0].children[1].children[1].classList.add('hidden')
document.getElementById('section_hello')
document.getElementById('section_hello').classList
document.getElementById('section_hello').classList.remove('hidden')
 
document.getElementById('heading_hello')
document.getElementById('heading_hello').innerHTML
document.getElementById('heading_hello').innerHTML = 'ABCDE'
 
// Tippen Sie einen Namen ins Eingabefeld.
document.getElementById("input_name")
document.getElementById("input_name").value
 
'Hello, ' + document.getElementById("input_name").value + '!'
`Hello, {document.getElementById('input_name').value}!`
`Hello, ${document.getElementById('input_name').value}!`
</source>
 
Erzeugen Sie nun in VSC eine leere Datei <code>web/main.js</code>, die den JavaScript-Code aufnehmen wird, den Sie gerade getestet haben.
Das geht übrigens auch mit einem Unix-Befehl ganz einfach:
<source lang="javascript">
touch web/main.js
</source>


Fügen Sie folgenden JavaScript-Code in diese Datei ein:
<source lang="javascript">
<source lang="javascript">
/**
/**
  * Welcomes the user of the web app by displaying a welcome message
  * Welcomes the user of the web app by displaying a welcome message
  * that includes his name. The name is fetched from a text input field.
  * that includes her name. The name is fetched from a text input field.
  */
  */
function sayHello()
function sayHello()
{ document.getElementById('heading_hello').innerHTML =
{ document.getElementById('heading_hello').innerHTML =
     'Hello, ' + document.getElementById("input_name").value + '!';
     `Hello, ${document.getElementById('input_name').value}!`;
  //'Hello, ' + document.getElementById("input_name").value + '!';


   document.getElementById('section_form') .classList.add  ('hidden');
   document.getElementById('section_form') .classList.add  ('hidden');
Zeile 157: Zeile 228:
/**
/**
  * Initializes the web app.
  * Initializes the web app.
  * Is to be called when the web app has been loaded completely.
  * To be called when the web app is fully loaded.
  */
  */
function init()
function init()
Zeile 164: Zeile 235:
}
}


// So call init, when you are ready with loading.
// Call init when the loading process has been completed.
window.addEventListener('load', init);
window.addEventListener('load', init);
</source>
</source>
Testen Sie die erste Funktion, indem Sie sie vollständig in das Konsolfester Ihresr Browsers einfügen und und führen Sie anschließend folgende Befehle aus:
<source lang="javascript">
window
sayHello;
sayHello();
</source>
Wie erklären Sie sich die jeweiligen Ergebnisse?


Im diesem Stückchen JavaScript-Code sind mehrere interessante Dinge zu entdecken.
Im diesem Stückchen JavaScript-Code sind mehrere interessante Dinge zu entdecken.


# Alle Befehle innerhalb einer JavaScript-Datei werden der Reihe nach abgearbeitet, sobald sie geladen wird. In der Datei <code>main.js</code>gibt es insgesamt drei Befehle: Zwei Funktionsdefinitionen und eine Wertzuweisung.
# Alle Befehle innerhalb einer JavaScript-Datei werden der Reihe nach abgearbeitet, sobald sie geladen wird. In der Datei <code>main.js</code> gibt es insgesamt drei Befehle: Zwei Funktionsdefinitionen und eine Wertzuweisung.
# Achtung: Die beiden Funktionen <code>sayHello</code>und <code>init</code>werden nur definiert, aber nicht sofort ausgeführt! Damit sie eine Wirkung entfalten, müssen sie aufgerufen werden.
# Achtung: Die beiden Funktionen <code>sayHello</code> und <code>init</code> werden nur definiert, aber nicht sofort ausgeführt! Damit sie eine Wirkung entfalten, müssen sie aufgerufen werden.
#Bei <code>sayHello</code>und <code>init</code>handelt es sich um sogenannte [[Observer]]-Funktionen. Derartige Funktionen werden immer dann aktiviert, wenn ein bestimmtes [[Ereignis (OOP)|Ereignis]] eintritt.  
#Bei <code>sayHello</code> und <code>init</code> handelt es sich um sogenannte [[Observer]]-Funktionen. Derartige Funktionen werden immer dann aktiviert, wenn ein bestimmtes [[Ereignis (OOP)|Ereignis]] eintritt.  
#Mittels des letzten JavaScript-Befehls, wird festgelegt, dass  die Init-Funktion ausgeführt wird, sobald die Web-Anwendung vollständig geladen ist, {{dh}}, sobald der Browser-Fenster <code>window</code> das Ereignis <code>'load'</code> signalisiert.
#Mittels des letzten JavaScript-Befehls, wird festgelegt, dass  die Init-Funktion ausgeführt wird, sobald die Web-Anwendung vollständig geladen ist, {{dh}}, sobald der Browser-Fenster <code>window</code> das Ereignis <code>'load'</code> signalisiert.
# Sobald Init-Funktion aufgerufen wird, wird festgelegt, dass die Funktion <code>sayHello</code>ausgeführt wird, wenn der Benutzer den Submit-Button drückt. Das heißt, solange die Init-Funktion nicht ausgeführt wurde, {{dh}}, solange das Dokument und alle seine assoziierten Dokumente nicht vollständig geladen wurden, hat ein Klick des Benutzers auf den Submit-Button keinerlei Auswirkungen.  
# Sobald Init-Funktion aufgerufen wird, wird festgelegt, dass die Funktion <code>sayHello</code> ausgeführt wird, wenn der Benutzer den Submit-Button drückt. Das heißt, solange die Init-Funktion nicht ausgeführt wurde, {{dh}}, solange das Dokument und alle seine assoziierten Dokumente nicht vollständig geladen wurden, hat ein Klick des Benutzers auf den Submit-Button keinerlei Auswirkungen.  
# Beide Funktionen machen regen Gebrauch vom JavaScript-Objekt <code>document</code>, das das [[Document Object Model]], {{dh}} die interne Darstellung des HTML-Dokuments als sogenannten DOM-Baum beinhaltet (siehe [https://developer.mozilla.org/en-US/docs/Web/API/Document MDN-API-Dokumentation]).  
# Beide Funktionen machen regen Gebrauch vom JavaScript-Objekt <code>document</code>, das das [[Document Object Model]], {{dh}} die interne Darstellung des HTML-Dokuments als sogenannten DOM-Baum beinhaltet (siehe [https://developer.mozilla.org/en-US/docs/Web/API/Document MDN-API-Dokumentation]).  
# Mit Hilfe der  Methode <code>getElementById</code>kann man besonders elegant auf bestimmte Elemente des DOM-Baus zugreifen, sofern man zuvor im HTML-Dokument für die entsprechenden Elemente <code>id</code>-Attribute definiert hat (was wir gemacht haben).
# Mit Hilfe der  Methode <code>getElementById</code> kann man besonders elegant auf bestimmte Elemente des DOM-Baus zugreifen, sofern man zuvor im HTML-Dokument für die entsprechenden Elemente <code>id</code>-Attribute definiert hat (was wir gemacht haben).
#Für jedes Element des DOM-Baums gibt es diverse elementspezifische [[Attribut]]e:
#Für jedes Element des DOM-Baums gibt es diverse elementspezifische [[Attribut]]e:
#*So kann man mittels <code>innerHTML</code>auf den HTML-Code eines Elements wie  <code>p</code>(Paragraph), <code>h1</code>(Hauptüberschrift), <code>h2</code>(Überschrift der 2. Stufe) etc. lesend und schreibend zugreifen.  
#*So kann man mittels <code>innerHTML</code> auf den HTML-Code eines Elements wie  <code>p</code> (Paragraph), <code>h1</code> (Hauptüberschrift), <code>h2</code> (Überschrift der 2. Stufe) etc. lesend und schreibend zugreifen.  
#*Das Attribut <code>value</code>ermöglicht einen lesenden und schreibenden Zugriff auf den Inhalt von Formularfeldern.   
#*Das Attribut <code>value</code> ermöglicht einen lesenden und schreibenden Zugriff auf den Inhalt von Formularfeldern.   
#*Über das Attribut <code>classList</code>hat man Zugriff auf <code>class</code>-Attribute, die einem HTML-Element zugeordnet sind. Man kann jedem Element neue <code>class</code>-Attribut-Werte zuordnen und bestehende Werte entfernen.  
#*Über das Attribut <code>classList</code> hat man Zugriff auf <code>class</code>-Attribute, die einem HTML-Element zugeordnet sind. Man kann jedem Element neue <code>class</code>-Attribut-Werte zuordnen und bestehende Werte entfernen.  
#*Wir nutzen das aus, indem wir die Formularabschnitt  <code>section_form</code>unsichtbar und dafür den Begrüßungsabschnitt  <code>section_hello</code>sichtbar machen, sobald die Funktion  <code>sayHello</code>ausgeführt wird.
#*Wir nutzen das aus, indem wir die Formularabschnitt  <code>section_form</code> unsichtbar und dafür den Begrüßungsabschnitt  <code>section_hello</code> sichtbar machen, sobald die Funktion  <code>sayHello</code> ausgeführt wird.
# Der dritte Befehl „<code>window.addEventListener('load', init);</code>“ übergibt die [[Funktion]] <code>init</code>(und nicht etwa den [[Funktionsaufruf]] <code>init()</code>) dem JavaScript-Objekt <code>window</code>mit der Bitte, diese Methode auszuführen, sobald das <code>load</code>-Ereignis eintritt. Das hat zur Folge, dass die Funktion  <code>init</code> nicht sofort aufgerufen wird, sondern erst – durch das Observer-Objekt <code>window</code> – sobald das Ereignis „der Inhalt des aktuelle Browserfensters wurde vollständig geladen“ eintritt (siehe [https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload MDN-API-Dokumentation]).
# Der dritte Befehl „<code>window.addEventListener('load', init);</code>“ übergibt die [[Funktion]] <code>init</code> (und nicht etwa den [[Funktionsaufruf]] <code>init()</code>) dem JavaScript-Objekt <code>window</code> mit der Bitte, diese Methode auszuführen, sobald das <code>load</code>-Ereignis eintritt. Das hat zur Folge, dass die Funktion  <code>init</code> nicht sofort aufgerufen wird, sondern erst – durch das Observer-Objekt <code>window</code>  – sobald das Ereignis „der Inhalt des aktuelle Browserfensters wurde vollständig geladen“ eintritt (siehe [https://developer.mozilla.org/en-US/docs/Web/Events/load MDN-API-Dokumentation]).
# Auch für den Submitbutton wird ein [[Eventlistener]] registriert. Die Funktion <code>sayHello</code>soll ausgeführt werden, sobald für diesen Button das  <code>click</code>-Ereignis eintritt. Diese Zuordnung kann allerdings erst passieren, wenn sich der Submit-Button auch im DOM-Baum befindet. Das ist jedoch sicher erst der Fall, wenn das gesamte Dokument geladen wurde. Daher wird diese Zuordnung nicht sofort, sondern in der Init-Funktion durchgeführt. (Das  <code>window</code>-Objekt existiert dagegen von Anfang an, {{dh}} auch wenn der DOM-Baum noch nicht geladen wurde.)
# Auch für den Submitbutton wird ein [[Eventlistener]] registriert. Die Funktion <code>sayHello</code> soll ausgeführt werden, sobald für diesen Button das  <code>click</code>-Ereignis eintritt. Diese Zuordnung kann allerdings erst passieren, wenn sich der Submit-Button auch im DOM-Baum befindet. Das ist jedoch sicher erst der Fall, wenn das gesamte Dokument geladen wurde. Daher wird diese Zuordnung nicht sofort, sondern in der Init-Funktion durchgeführt. (Das  <code>window</code>-Objekt existiert dagegen von Anfang an, {{dh}} auch wenn der DOM-Baum noch nicht geladen wurde.)
# Alle Funktionen wurden mit Kommentaren im [[JSDoc (GitHub)|JSDoc]]-Format versehen.<ref>{{Quelle|JSDoc (GitHub)}}</ref> Machen Sie das auch immer. Andere Entwickler und auch Sie selbst werden das schätzen, wenn sie bzw. Sie den Code zu einem späteren Zeitpunkt lesen und verstehen müssen. Das JSDoc-Format bring den Vorteil mit sich, dass Sie automatisch eine Schnittstellen-Dokumentation für Ihre Anwendung erstellen können  
# Alle Funktionen wurden mit Kommentaren im [[JSDoc (GitHub)|JSDoc]]-Format versehen.<ref>{{Quelle|JSDoc (GitHub)}}</ref> Machen Sie das auch immer. Andere Entwickler und auch Sie selbst werden das schätzen, wenn sie bzw. Sie den Code zu einem späteren Zeitpunkt lesen und verstehen müssen. Das JSDoc-Format bring den Vorteil mit sich, dass Sie automatisch eine Schnittstellen-Dokumentation für Ihre Anwendung erstellen können  


Wenn Sie jetzt Ihre Anwendung testen, hat sich nichts geändert.
Wenn Sie jetzt Ihre Anwendung testen, hat sich nichts geändert.
Der Grund ist wie bei der Datei <code>main.css</code>“ auch,
Der Grund ist wie bei der Datei <code>main.css</code>,
dass die  Datei <code>index.html</code>nichts davon weiß,
dass die  Datei <code>index.html</code> nichts davon weiß,
das ihr dieser JavaScript-Code zugeordnet ist. Das muss ihr erst bekannt gegeben werden.
das ihr dieser JavaScript-Code zugeordnet ist. Das muss ihr erst bekannt gegeben werden.
Fügen Sie folgende Zeile in der Head-Bereich der <code>index.html</code> ein:
Fügen Sie folgende Zeile in der Head-Bereich der <code>index.html</code> ein:


<source lang="html5">
<source lang="html5">
<script type="text/javascript" src="main.js"></script>
<script src="main.js" async></script>
</source>
</source>


Nun wird nicht nur die CSS-Datei, sondern auch die JavaScript-Datei geladen,
Nun wird nicht nur die CSS-Datei, sondern auch die JavaScript-Datei geladen,
bevor der Body-Bereich der  <code>index.html</code> eingelesen wird.
bevor der Body-Bereich der  <code>index.html</code> eingelesen wird.
Beachten Sie, dass folgender Code zwar XML-konform, aber in Standard-HTML5-Dokumenten nicht erlaubt ist:
Die Angabe von <code>async</code> bewirkt Folgendes: Das Rendering
der Seite wird durch das Laden des Skripts '''nicht''' unterbrochen. Das heißt, wenn ein langes Skript ohne
Angabe von  <code>async</code> geladen wird, kann sich der Seitenaufbau merklich verzögern.
Mit Angabe dieser Option ist dies nicht der Fall.
 
Sobald das Skript geladen wurde, wird es ausgeführt.
Zu diesem Zeitpunkt ist der Rendervorgang möglicherweise noch nicht abgeschlossen. Das
heißt, man muss darauf achten, dass man von Skript aus nicht zu früh auf die Elemente des
HTML-Dokuments zugreift. Aus diesem Grund wird die <code>init</code>-Funktion
am Ende der Datei <code>main.css</code> nicht direkt mittels <code>init()</code>
gestartet, sondern mittels eines Eventlisteners <code>window.addEventListener('load', init)</code>.
 
Beachten Sie, dass folgender Code zwar SGML/XML-konform, aber in Standard-HTML5-Dokumenten nicht erlaubt ist:
<source lang="html5">
<source lang="html5">
<script type="text/javascript" src="main.js"/>
<script src="main.js" async="async"/>
</source>
</source>
Der Schrägstrich am Ende des Tags ist auch (self closing tag) SGML/XML-, aber nicht HTML5-konform. Die Brwoser sind hinsichtlich der Einhaltung von Konfotmitätsregel allerdings nicht sonderlich pedantisch. Sie interpretieren auc nicht-konformen HTML-Code. Allerdings können dabei versciedene Brwoser durchaus zu verschiedenen Interpretationen kommen.


Die tieferen Gründe dafür werden sehr schön auf
Die tieferen Gründe dafür werden sehr schön auf
[http://stackoverflow.com/questions/69913/why-dont-self-closing-script-tags-work Stack Overflow] beschrieben.
[https://stackoverflow.com/questions/69913/why-dont-self-closing-script-tags-work Stack Overflow] beschrieben.
(Ich verstehe es trotzdem nicht.)


Testen Sie Ihre Anwendung nochmals. Sie sollten jetzt Ihren Namen in das Textfeld
Testen Sie Ihre Anwendung nochmals. Sie sollten jetzt Ihren Namen in das Textfeld
Zeile 212: Zeile 308:
==Barrierefreiheit==
==Barrierefreiheit==


Gemäß den „Richtlinien für barrierefreie Webinhalte“<ref>{{Quelle|W3C (2009)}}</ref>, Abschnitt 2.1 soll eine Web-Entwickler dafür Sorge tragen,
Gemäß den „Richtlinien für barrierefreie Webinhalte“<ref>{{Quelle|W3C (2018)}}</ref>, Abschnitt 2.1 soll eine Web-Entwickler dafür Sorge tragen,
„dass alle Funktionalitäten per Tastatur zugänglich sind“. Das ist bei unserer Web-Anwendung nicht in voller Schönheit der Fall. Der Cursor befindet sich wegen
dass „alle Funktionalitäten per Tastatur zugänglich“ sind ( “Make all functionality available from a keyboard. ”). Das ist bei unserer Web-Anwendung nicht in voller Schönheit der Fall. Der Cursor befindet sich wegen
des Attributes „<code>autofocus="autofocus"</code>“ im Text-Input-Feld des HTML-Dokument bei Start der Anwendung automatisch an der richtigen Position.
des Attributes „<code>autofocus</code>“ im Text-Input-Feld des HTML-Dokument bei Start der Anwendung automatisch an der richtigen Position.
Man muss also den Cursor nicht erst mit Hilfe der Maus platzieren. Auch die Weiterschaltung mittels Tab-Taste funktioniert. Man kann zunächst seinen
Man muss also den Cursor nicht erst mit Hilfe der Maus platzieren. Auch die Weiterschaltung mittels Tab-Taste funktioniert. Man kann zunächst seinen
Namen eingeben, dann zweimal die Tab-Taste betätigen, um den Submit-Button zu aktivieren und dann Return drücken, um ein Klick-Event für diesen Button
Namen eingeben, dann zweimal die Tab-Taste betätigen, um den Submit-Button zu aktivieren und dann Return drücken, um ein Klick-Event für diesen Button
Zeile 224: Zeile 320:
Damit begeben wir uns in die Untiefen der Benutzerinteraktion mit dem Browser...
Damit begeben wir uns in die Untiefen der Benutzerinteraktion mit dem Browser...


Um diese Funktionalität zu realisieren, brauchen wir einen weiteren Eventlistener. Die Observer-Methode heiße <code>sayHelloOnEnter</code>.
Um diese Funktionalität zu realisieren, brauchen wir einen weiteren Eventlistener. Die Observer-Methode heiße <code>sayHelloOnEnter</code>.
Diese muss wieder registriert werden. Die Tastaturereignisse werden vom <code>window</code>-Objekt gemeldet
Diese muss wieder registriert werden. Die Tastaturereignisse werden vom <code>window</code>-Objekt gemeldet
(und nicht etwa vom <code>dokument</code>-Objekt, da das Dokument nichts mit Tastatureingaben zu schaffen hat).
(und nicht etwa vom <code>dokument</code>-Objekt, da das Dokument nichts mit Tastatureingaben zu schaffen hat).
Zeile 237: Zeile 333:
(Dieser Punkt wird später noch wesentlich genauer behandelt.)
(Dieser Punkt wird später noch wesentlich genauer behandelt.)


Die (in diesem Teil des Tutoriums noch globale) Funktion  <code>sayHelloOnEnter</code>wird folgendermaßen definiert:
Die (in diesem Teil des Tutoriums noch globale) Funktion  <code>sayHelloOnEnter</code> wird folgendermaßen definiert:


<source lang="javascript">
<source lang="javascript">
/**
* A keyboard event observer. It tests whether the enter key has been pressed.
* If so, the sayHello method is activated. Default reactions of the browser are
* disabled.
* @param {KeyboardEvent} p_event - A standard JavaScript keyboard event object
*  (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent)
*/
function sayHelloOnEnter(p_event)
function sayHelloOnEnter(p_event)
{ if (p_event.code === 'Enter' &&
{ if (p_event.key === 'Enter')  // The enter key has been pressed.
      document.activeElement === document.getElementById('input_name')  // focus is on 'input_name'
   { // Prevent other handlers from handling the keypress event.
    )
    p_event.preventDefault();
   { p_event.preventDefault();
     p_event.stopPropagation();
     p_event.stopPropagation();
     sayHello();
 
     // If the input text field or the submit button is active, say hello.
    if (document.activeElement === document.getElementById('input_name') ||
        document.activeElement === document.getElementById('button_submit')
      )
    { sayHello(); }
 
    // If the reset button is active, clear the input text field.
    else if (document.activeElement === document.getElementById('button_reset'))
    { document.getElementById('input_name').value = ''; }
   }
   }
}
}
</source>
</source>


Vier Dinge fallen bei dieser Definition auf:
Man kann die Tests noch deutlich vereinfachen, da <code>p_event</code> im Attribut
<code>target</code> das HTML-Element enthält, das beim Tastendruck aktiv war.
Außerdem löst der aktive Buttons von aktuellen Browsern von sich aus beim Betätigen
der Entertaste einen Click-Event aus. Das heißt, wir brauchen die Reaktion auf den Druck
der Entertaste für die Buttons gar nicht zu implementieren.


# Die Funktion hat einen Parameter namens <code>p_event</code>(Parameternamen können beliebig gewählt werden. Ich beginne Parameternamen stets mit einem <code>p_</code>, um rein optisch klar zu machen, dass es sich um einen Parameter handelt; siehe [[Multimedia-Programmierung: Style Guide]]). In diesem Parameter übermittelt das <code>window</code>-Objekt dem Eventhandler nähere Informationen zum aktuellen Tastaturereignis: welche Taste gedrückt wurde, ob die Schift-Taste dabei gehalten wurde etc. Details finden sich in der ausgezeichneten [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent MDN-JavaScript-Dokumentation].
<source lang="javascript">
#Im Funktionsrumpf wird getestet, ob die Returntaste gedrückt wurde. Falls das Ergebnis des Test „false“ lautetet, macht sie nichts (da das Zeichen einfach ins Textfeld eingefügt werden soll), anderenfalls ruft sie die Funktion <code>sayHello</code>auf, um die Eingabe abzuschließen.  
function sayHelloOnEnter(p_event)
#Die Gleichheit wird mit „<code>==&#61;</code>“  anstelle von „<code>==</code>“  getestet. Bei dem ersten Test handelt es sich um einen strikten Gleichheitstest.  Der zweite Test liefert dagegen manchmal ziemlich eigenwillige Ereignisse. So wird beispielsweise bei den Tests  „<code>0==&#39;&#39;</code>“ „<code>0=='0'</code>“ jeweils <code>true</code>als Ereignis ausgegeben. Der strikte Gleichheitstest liefert dagegen das erwartete Ergebnis <code>false</code>.
{ // If the enter key has been pressed while the focus is on text input field, say hello.
# Man muss höllisch aufpassen, wenn man Tastatur-Events abfängt. Wenn der Fokus nicht auf dem Textfeld, sondern auf dem Reset-Button liegt, darf man die Funktion der Enter-Taste nicht ändern. Der Reset-Button kann mittels der Tab-Taste selektiert und mittels Enter-Taste aktiviert werden (barrierefreie Formulareingabe ohne Maus). Daher darf die Bedeutung der Enter-Taste nur verändert werden, wenn der Fokus auf dem Texteingabefeld liegt.
  if (p_event.key === 'Enter' && p_event.target.id === 'input_name') 
  { // Prevent other handlers from handling the keypress event.
    p_event.preventDefault();
    p_event.stopPropagation();
    sayHello();
  } 
}
</source>
 
Fünf Dinge fallen bei dieser Definition auf:
 
# Die Funktion hat einen Parameter namens <code>p_event</code> (Parameternamen können beliebig gewählt werden. Ich beginne Parameternamen stets mit einem <code>p_</code>, um rein optisch klar zu machen, dass es sich um einen Parameter handelt; siehe [[Web-Programmierung: Style Guide]]). In diesem Parameter übermittelt das <code>window</code>-Objekt dem Eventhandler nähere Informationen zum aktuellen Tastaturereignis: welche Taste gedrückt wurde, ob die Shift-Taste dabei gehalten wurde etc. Details finden sich in der ausgezeichneten [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent MDN-JavaScript-Dokumentation].
#Im Funktionsrumpf wird getestet, ob die Returntaste gedrückt wurde. Falls das Ergebnis des Test false lautetet, macht sie nichts (da das Zeichen einfach ins Textfeld eingefügt werden soll), anderenfalls ruft sie die Funktion <code>sayHello</code> auf, um die Eingabe abzuschließen.  
#Die Gleichheit wird mit „<code>==&#61;</code>“  anstelle von „<code>==</code>“  getestet. Bei dem ersten Test handelt es sich um einen strikten Gleichheitstest.  Der zweite Test liefert dagegen manchmal ziemlich eigenwillige Ereignisse. So wird beispielsweise bei den Tests  „<code>0 == &#39;&#39;</code>“ „<code>0 == '0'</code>“ jeweils <code>true</code> als Ereignis ausgegeben. Der strikte Gleichheitstest liefert dagegen das erwartete Ergebnis <code>false</code>.
# Die Bedeutung der Enter-Taste muss abhängig vom Fokus verändert werden, da dieser mittels der Tab-Taste geändert werden kann. Wenn der Fokus auf dem Texteingabefeld oder dem Submit-Button liegt, soll der Benutzer gegrüßt werden. Liegt er dagegen auf der Reset-Taste, wird das Eingabefeld geleert. Bei den beiden Buttons ist dies das Defaultverhalten, für das Textfeld muss es implementiert werden.
# Im Code wird <code>p_event.key === 'Enter'</code> anstelle von <code>p_event.keyCode === 13</code> verwendet. Die letztere Methode gilt als deprecated, da sie die Position der gedrückten Taste auf der Tastatur ausgibt. Diese Position ist jedoch sprachabhängig. Beispielsweise hat die Z-Taste auf einer amerkikansischen Tastatur eine andere Position als auf einer deutschen. Mit der folgenden [https://jsfiddle.net/kowarschick/3e0L2cna/ JSFiddle-Anwendung] können Sie die Unterschiede zwischen <code>p_event.key</code>, <code>p_event.code</code> und <code>p_event.keyCode</code> untersuchen. Öffnen Sie die Anwendung in verschiedenen Browsern und drücken Sie unterschiedliche Tasten ({{zB}} auch im Nummernblock.


So weit so gut. Aber wenn man <code>sayHelloOnEnter</code> so definiert, dass sie bei einem Druck der Returntaste lediglich die Funktion
So weit so gut. Aber wenn man <code>sayHelloOnEnter</code> so definiert, dass sie bei einem Druck der Returntaste lediglich die Funktion
<code>sayHello</code>aufruft,
<code>sayHello</code> aufruft oder das Eingabefeld löscht,
stellt man schnell fest, dass ein Betätigen der Returntaste nicht wie gewünscht funktioniert. Das Problem ist, dass der Browser Tastatur-Ereignisse bereits anderweitig verarbeitet. Je nach Tastendruck werden Textfelder befüllt, Formularelemente aktiviert oder irgendwelche Spezialfunktionen ausgeführt. Man muss den JavaScript-Interpreter
stellt man eventuell fest, dass ein Betätigen der Returntaste nicht wie gewünscht funktioniert. Das Problem ist, dass der Browser Tastatur-Ereignisse bereits anderweitig verarbeitet. Je nach Tastendruck werden Textfelder befüllt, Formularelemente aktiviert oder irgendwelche Spezialfunktionen ausgeführt. Man muss dem JavaScript-Interpreter
klar machen, dass er dies im Falle der Returntaste nicht machen soll. Das ist Aufgabe der beiden Befehle <code>p_event.preventDefault();</code>“ und „<code>p_event.stopPropagation();</code>“. Der erste verlangt, dass bei Drücken dieser Taste keine Default-Aktionen ausgeführt werden sollen und der zweite bewirkt, dass  
klar machen, dass er dies im Falle der Returntaste nicht machen soll. Das ist Aufgabe der beiden folgenden Befehle, die in den Eventhandler
eingefügt wurden:
 
<source lang="javascript">
p_event.preventDefault();
p_event.stopPropagation();
</source>  
 
Der erste verlangt, dass bei Drücken dieser Taste keine Default-Aktionen ausgeführt werden sollen und der zweite bewirkt, dass  
das Ereignis nicht auch noch von irgendwelchen anderen Eventhandlern behandelt wird.
das Ereignis nicht auch noch von irgendwelchen anderen Eventhandlern behandelt wird.
Fügen Sie also die beiden Befehle noch an den Anfang des Rumpfes der If-Anweisung in <code>sayHelloOnEnter</code> ein.<!--


Damit sollte alle funktionieren wie geplant. Denkste! In (zumindest manchen) Browsern (zumindest mancher) mobiler Devices bewirkt ein Betätigen der
Damit sollte alle funktionieren wie geplant. Denkste! In (zumindest manchen) Browsern (zumindest mancher) mobiler Devices bewirkt ein Betätigen der
Zeile 279: Zeile 404:
In derartigen Apps sind die Defaultaktionen des Browsers deaktiviert (oder können zumindest leicht deaktiviert werden).
In derartigen Apps sind die Defaultaktionen des Browsers deaktiviert (oder können zumindest leicht deaktiviert werden).


Die Lösung des Problems ist hier allerdings einfacher. Man muss  
Die Lösung des Problems ist hier allerdings recht einfach. Man muss  
<source lang="javascript">
<source lang="javascript">
  if (p_event.code === 'Enter'  || p_event.keyCode === 13)
if (p_event.key === 'Enter'  || p_event.keyCode === 13)
</source>
</source>
anstelle von  
anstelle von  
<source lang="javascript">
<source lang="javascript">
  if (p_event.code === 'Enter' )
if (p_event.key === 'Enter' )
</source>
</source>
schreiben. Die Verwendung von  „<code>p_event.keyCode</code>“ gilt eigentlich als veraltet. Sie ist als “deprecated” („veraltet“, „überholt“, „missbilligt“) [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode gekennzeichnet].
schreiben. Die Verwendung von  „<code>p_event.keyCode</code>“ gilt eigentlich als veraltet. Sie ist als [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode “deprecated” („veraltet“, „überholt“, „missbilligt“) gekennzeichnet].
Man soll stattdessen „<code>p_event.code</code>“ verwenden. Dieses Attribut wird allerdings von vielen aktuellen Browsern noch nicht richtig interpretiert...
Man soll stattdessen „<code>p_event.key</code>“  oder <code>p_event.code</code>“ (Unterschied: siehe [https://codepen.io/denilsonsa/pen/epmoma codepen.io]) verwenden. Diese Attribute sind nun hoffentlich in allen aktuellen Browsern richtig implementiert worden. Überprüfen kann man dies, indem man folgende URL in diversen Browsern öffnet:
(Hier tauchen wieder die Browser-Inkompatibilitäten auf, die man mit dem Erscheinen von HTML5 und EcmaScript 5 eigentlich schon als überwunden glaubte.)
  https://jsfiddle.net/kowarschick/3e0L2cna/
<!--
(Hier tauchen zumindest im Jahr 2018 wieder Browser-Inkompatibilitäten auf, die man mit dem Erscheinen von HTML5 und ECMAScript 5 eigentlich schon als überwunden glaubte.)
-->
 
<strong>Fazit:</strong> Bis eine Web-Anwendung wirklich so läuft, wie man sich das vorstellt, vergeht viel Zeit.
Gerade bei mobilen End-Geräten hilft nur testen, testen, testen ...
 
Vergessen Sie nicht, Ihr aktuelles Projekt auf dem Git-Server zu speichern, sobald alles funktioniert.
 
==Fortsetzung des Tutoriums==
==Fortsetzung des Tutoriums==


Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World 04|Teil 4 des Tutoriums]] bearbeiten.
Sie sollten nun [[HTML5-Tutorium: JavaScript: Hello World 04|Teil 4 des Tutoriums]] bearbeiten.


Vergessen Sie nicht, vorher Ihr aktuelles Projekt im Repository zu speichern.
-->
==Quellen==
==Quellen==
<references/>
<references/>
<ol>
<ol>
<li value="3">{{Quelle|Kowarschick, W.: Multimedia-Programmierung}}</li>
<li value="3">{{Quelle|Kowarschick, W.: Web-Programmierung}}</li>
</ol>
</ol>
<noinclude>[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]</noinclude>
[[Kategorie: HTML5-Tutorium: JavaScript: Hello World]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]

Aktuelle Version vom 23. März 2024, 18:15 Uhr

Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:

Korrektheit: 3
(zu größeren Teilen überprüft)
Umfang: 4
(unwichtige Fakten fehlen)
Quellenangaben: 3
(wichtige Quellen vorhanden)
Quellenarten: 5
(ausgezeichnet)
Konformität: 3
(gut)

Vorlesung WebProg

Inhalt | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6

Musterlösung: index.html (Git-Repository, HTML validate, CSS validate)

Anwendungsfälle (Use Cases)

Gegenüber dem zweiten Teil des Tutoriums ändern sich die die Anwendungsfälle deutlich. Es soll nun nicht mehr die ganze Welt begrüßt werden, sondern der Benutzer, der die Web-Anwendung gestartet hat. Dazu muss er zunächst nach seinem Namen gefragt werden. Anschließend wird das HTML-Dokumnt mit Hilfe von JavaScript umgestaltet: Das Eingabeformular wird ausgeblendet und stattdessen wird die Begrüßungsformel angezeigt.

Erstellen eines neuen Projekts

Erstellen Sie ein neues Projekt hello_world_03 als Fork von hello_world_02 (siehe Hello-World-02-Tutorium).

Single-Page-Web-Anwendung

Die Anwendung wird als Single-Page-Web-Anwendung (Onepager) realisiert. Das HTML-Dokument index.html enthält zwei Abschnitte (sections), eines mit einem Formular zur Eingabe des Namens und ein zweites zur Begrüßung des Benutzers, nachdem er seinen Namen eingegeben hat.

Für jeden Abschnitt fügen wir in index.html eine HTML-Section „<section id="..."> ...</section>“ ein. Das Section-Element gruppiert einen logischen Abschnitt oder ein Kapitel eines HTML-Dokuments. Es soll laut Spezifikation eine Überschrift enthalten, die das Thema des Abschnitts beschreibt.

Man hätte auch ein Div-Element „<div id="..."> ...</div>“ anstelle des Section-Elements verwenden können. Ein Div-Element hat keinerlei Semantik (Bedeutung), es dient lediglich der Strukturierung einer HTML-Datei. Die (spezifikationsgemäße) Verwendung von Section- und Article-Elementen, die in HTML5 eingeführt wurden, hat den Vorteil, dass Browser deren Bedeutung kennen und daher geeignete Defaultstyles verwenden können, falls der Entwickler keine entsprechenden Styles angibt. Ein Beispiel sind Browser für Blinde. Diese Browser lesen die Inhalte entweder vor oder geben sie textuell über eine sogenannte Braillezeile aus. Für derartige Browser werden meist keine CSS-Layout-Vorgaben gemacht (obwohl dies problemlos möglich wäre) und daher ist es wichtig, Strukturelementen eine Semantik zuzuordnen. Ein Blindenbrowser könnte beim Vorlesen eines Dokuments beispielsweise stets das Wort „Kapitel“ vor eine H1-Überschrift einfügen, die als erstes Element innerhalb eines Section-Elements steht.

<body>
  <section id="section_form">
    <h1>Hello, Stranger!</h1>
  </section>
  <section id="section_hello" class="hidden">
    <h1 id="heading_hello">Hello, ...!</h1>
    <p>Welcome to Full Stack Web Development!</p>
  </section>
</body>

Wenn Sie diese Datei ausführen, stellen Sie fest, dass sich nicht viel geändert hat. Anstelle einer Überschrift werden nun zwei angezeigt.

Allerdings wurden drei id-Attribute in das Dokument eingefügt: „id="section_form"“, „id="section_hello"“ und „id="heading_hello"“. Jedes öffnende HTML-Element darf mit einem derartigen Attribut versehen werden. Beachten Sie, das es in einer HTML-Datei keine zwei id-Attribute mit demselben Namen geben darf.

Die Vergabe von id-Attributen bringt zwei Vorteile mit sich: Zum einen können so bestimmte HTML-Element gezielt mittels CSS gestylt werden, und zum anderen können bestimmte HTML-Element gezielt mittels JavaScript modifiziert werden.

Im Browser soll zunächst nur der erste Abschnitt mit dem Formular angezeigt werden (id="section_form"). Um das zu erreichen, fügen Sie zunächst folgenden Code in die CSS-Datei ein:

.hidden
{ display: none; }

Fügen Sie nun in das öffnende Tag des Section-Elements mit dem Identifikator section_hello das Attribut-Wert-Paar „class="hidden"“ ein. Ein class-Attribut darf im Gegensatz zu einem id-Attribut beliebig vielen Elementen zugeordnet werden, ohne dass sich der zugehörige Wert unterscheiden muss. id-Attribute werden verwendet, um HTML-Elemente eindeutig zu kennzeichnen, class-Attribute werden verwendet, um diversen HTML-Elementen gleiche CSS-Eigenschaften zuzuordnen. In einem Onepager sind üblicherweise die meisten Seiten unsichtbar. Daher ist hier ein class-Attribut angebracht.

In der CSS-Datei werden Identifikator-Attributewerte mit einer Raute „#“ gekennzeichnet (z. B. #section_form) und Klassen-Attributwerte mit einem Punkt „.“ (z. B. .hidden):

Führen Sie diese Änderungen durch und testen Sie die Web-Anwendung erneut. Nun sollte nur noch die Überschrift der Formular-Section zu sehen sein.

Definition eines HTML5-Formulars

Als nächstes muss das Formular erstellt werden, mittels dem der Name des Besuchers erfragt wird. Es soll folgende Elemente haben:

  • ein Label, das beschreibt, welche Information vom Benutzer eingegeben werden soll
  • ein Text-Feld, in das der Benutzer seinen Namen eingeben kann
  • einen Reset-Button, um den Inhalt des Namensfeldes zu löschen
  • einen Submit-Button, um dem Browser mitzuteilen, dass der Name vollständig eingegeben wurde

Der zugehörige HTML-Code sieht folgendermaßen aus:

<form>
  <div>
    <label for="input_name">What's your name?</label>
    <input id="input_name" autofocus>
  </div>
  <div>
    <input id="button_reset"  type="reset"  value="Reset">
    <input id="button_submit" type="button" value="Say hello">
  </div>
</form>

Er wird in die erste Section hinter die zugehörige Überschrift eingefügt.

In diesem Formular („<form> ... </form>“) sind die vier zuvor genannten Elemente enthalten. Je zwei davon sind mittels eines Div-Elements zu einer Gruppe zusammengefasst. Damit ist die Struktur des Formulars vorgegeben: Jedes Div-Element steht in einer eigenen Zeile, die darin enthaltenen Elemente stehen jeweils hintereinander in einer Zeile. Mittels CSS können die Elemente nun besser angeordnet werden: mehr Abstand von der Überschrift, mehr vertikaler Abstand zwischen den Elementen, einheitliche Breite der Elemente etc. Wie sagt Captain Picard ganz richtig: „Machen Sie es so.“

Beachten Sie, dass zwei weitere id-Attribute eingeführt wurden: „id="input_name"“ und „id="button_submit"“. Diese werden für den Zugriff von JavaScript aus auf das Dokument benötigt. Über den Identifikator input_name kann auf den Inhalt des Textfeldes, d. h. auf den Namen, den der Benutzer eingegeben hat, zugegriffen werden. Außerdem kann mit Hilfe dieses Identifikators das Label-Element über das Attribut for an das Textfeld gekoppelt werden. Damit weiß der Browser, auf welches Input-Element sich das Label-Element bezieht. Auch dies ist wieder besonders wichtig, wenn der Zusammenhang nicht optisch (per CSS) hergestellt werden kann bzw. hergestellt wird. Damit lassen sich aber auch Checkboxes realisieren, die durch einen Klick auf den zugehörigen Label aktiviert und wieder deaktiviert werden können.

Der Identifikator für den Submit-Button wird benötigt, um in JavaScript die Klick-Aktion des Benutzers abfangen zu können. Normalerweise im Form-Element in einem Attribut namens action ein URI angegeben. Dieser verweist auf eine Serveradresse, an den die vom Benutzer erfassten Daten bei einem Klick auf einen echten Submit-Button (type="submit") übermittelt werden. In dieser Web-Anwendung werden keine Daten an einen Server übermittelt. Alle Benutzereingaben werden direkt im Browser (per JavaScript) verarbeitet. Deshalb wird das Action-Attribut nicht benötigt und als Submit-Button wird ein einfacher Button (type="button") eingesetzt.

Sie können und sollten das Formular auch noch per CSS stylen:

p, label
{ font-family: "Times New Roman", Times, serif;
  font-size:   100% !important;
}

label, input
{ width:      10em;
  font-size:  100%;
  display:    inline-block;
  box-sizing: border-box;
  margin:     0.5ex;
}

label
{ text-align: right; }

#section_form
{ text-align:   center;
  margin-left:  auto;
  margin-right: auto;
}

#section_form > h1
{ margin-bottom: 0.5ex; }

#section_hello > p
{ font-size: 156.25% !important; /* 1.25^2 = 1.5625 */ }

Wenn alles zu Ihrer Zufriedenheit ausgefallen ist, sollten Sie den aktuellen Stand in Ihr Repository einspielen.

Interaktion mittels JavaScript

Öffnen Sie in Ihrem Browser die Musterlösung:

 index.html

Öffnen Sie im Browser die Entwicklerumgebung (Rechtsklick auf Web-Seite im Browser und Untersuchen anklicken).

Öffnen Sie im Browser die Konsole. Falls Sie dazu aufgefordert werden (Firefox) geben Sie 'Einfügen erlauben' als Befehl in das Konsofesnter ein.

Abschließend können Sie im Konsolfester verschiedene EcmaScript-Befehler ausprobieren. Kopeiren Sie der Reihe nach die einzelnen Befehle (Zeilen) ins Konsolfenster. Was bewirken sie?

document.getElementById('section_hello').classList.remove('hidden');
document.getElementById('section_hello').classList.add('hidden');

document
document.children
document.children[0]
document.children[0].children
document.children[0].children[1]
document.children[0].children[1].children
document.children[0].children[1].children[1]
document.children[0].children[1].children[1].classList
document.children[0].children[1].children[1].classList.remove('hidden')
document.children[0].children[1].children[1].classList.add('hidden')
document.getElementById('section_hello')
document.getElementById('section_hello').classList
document.getElementById('section_hello').classList.remove('hidden')

document.getElementById('heading_hello')
document.getElementById('heading_hello').innerHTML
document.getElementById('heading_hello').innerHTML = 'ABCDE'

// Tippen Sie einen Namen ins Eingabefeld.
document.getElementById("input_name")
document.getElementById("input_name").value

'Hello, ' + document.getElementById("input_name").value + '!'
`Hello, {document.getElementById('input_name').value}!`
`Hello, ${document.getElementById('input_name').value}!`

Erzeugen Sie nun in VSC eine leere Datei web/main.js, die den JavaScript-Code aufnehmen wird, den Sie gerade getestet haben. Das geht übrigens auch mit einem Unix-Befehl ganz einfach:

touch web/main.js

Fügen Sie folgenden JavaScript-Code in diese Datei ein:

/**
 * Welcomes the user of the web app by displaying a welcome message
 * that includes her name. The name is fetched from a text input field.
 */
function sayHello()
{ document.getElementById('heading_hello').innerHTML =
    `Hello, ${document.getElementById('input_name').value}!`;
  //'Hello, ' + document.getElementById("input_name").value + '!';

  document.getElementById('section_form') .classList.add   ('hidden');
  document.getElementById('section_hello').classList.remove('hidden');
}

/**
 * Initializes the web app.
 * To be called when the web app is fully loaded.
 */
function init()
{ document.getElementById('button_submit')
          .addEventListener('click', sayHello);
}

// Call init when the loading process has been completed.
window.addEventListener('load', init);

Testen Sie die erste Funktion, indem Sie sie vollständig in das Konsolfester Ihresr Browsers einfügen und und führen Sie anschließend folgende Befehle aus:

window
sayHello;
sayHello();

Wie erklären Sie sich die jeweiligen Ergebnisse?

Im diesem Stückchen JavaScript-Code sind mehrere interessante Dinge zu entdecken.

  1. Alle Befehle innerhalb einer JavaScript-Datei werden der Reihe nach abgearbeitet, sobald sie geladen wird. In der Datei main.js gibt es insgesamt drei Befehle: Zwei Funktionsdefinitionen und eine Wertzuweisung.
  2. Achtung: Die beiden Funktionen sayHello und init werden nur definiert, aber nicht sofort ausgeführt! Damit sie eine Wirkung entfalten, müssen sie aufgerufen werden.
  3. Bei sayHello und init handelt es sich um sogenannte Observer-Funktionen. Derartige Funktionen werden immer dann aktiviert, wenn ein bestimmtes Ereignis eintritt.
  4. Mittels des letzten JavaScript-Befehls, wird festgelegt, dass die Init-Funktion ausgeführt wird, sobald die Web-Anwendung vollständig geladen ist, d. h., sobald der Browser-Fenster window das Ereignis 'load' signalisiert.
  5. Sobald Init-Funktion aufgerufen wird, wird festgelegt, dass die Funktion sayHello ausgeführt wird, wenn der Benutzer den Submit-Button drückt. Das heißt, solange die Init-Funktion nicht ausgeführt wurde, d. h., solange das Dokument und alle seine assoziierten Dokumente nicht vollständig geladen wurden, hat ein Klick des Benutzers auf den Submit-Button keinerlei Auswirkungen.
  6. Beide Funktionen machen regen Gebrauch vom JavaScript-Objekt document, das das Document Object Model, d. h. die interne Darstellung des HTML-Dokuments als sogenannten DOM-Baum beinhaltet (siehe MDN-API-Dokumentation).
  7. Mit Hilfe der Methode getElementById kann man besonders elegant auf bestimmte Elemente des DOM-Baus zugreifen, sofern man zuvor im HTML-Dokument für die entsprechenden Elemente id-Attribute definiert hat (was wir gemacht haben).
  8. Für jedes Element des DOM-Baums gibt es diverse elementspezifische Attribute:
    • So kann man mittels innerHTML auf den HTML-Code eines Elements wie p (Paragraph), h1 (Hauptüberschrift), h2 (Überschrift der 2. Stufe) etc. lesend und schreibend zugreifen.
    • Das Attribut value ermöglicht einen lesenden und schreibenden Zugriff auf den Inhalt von Formularfeldern.
    • Über das Attribut classList hat man Zugriff auf class-Attribute, die einem HTML-Element zugeordnet sind. Man kann jedem Element neue class-Attribut-Werte zuordnen und bestehende Werte entfernen.
    • Wir nutzen das aus, indem wir die Formularabschnitt section_form unsichtbar und dafür den Begrüßungsabschnitt section_hello sichtbar machen, sobald die Funktion sayHello ausgeführt wird.
  9. Der dritte Befehl „window.addEventListener('load', init);“ übergibt die Funktion init (und nicht etwa den Funktionsaufruf init()) dem JavaScript-Objekt window mit der Bitte, diese Methode auszuführen, sobald das load-Ereignis eintritt. Das hat zur Folge, dass die Funktion init nicht sofort aufgerufen wird, sondern erst – durch das Observer-Objekt window – sobald das Ereignis „der Inhalt des aktuelle Browserfensters wurde vollständig geladen“ eintritt (siehe MDN-API-Dokumentation).
  10. Auch für den Submitbutton wird ein Eventlistener registriert. Die Funktion sayHello soll ausgeführt werden, sobald für diesen Button das click-Ereignis eintritt. Diese Zuordnung kann allerdings erst passieren, wenn sich der Submit-Button auch im DOM-Baum befindet. Das ist jedoch sicher erst der Fall, wenn das gesamte Dokument geladen wurde. Daher wird diese Zuordnung nicht sofort, sondern in der Init-Funktion durchgeführt. (Das window-Objekt existiert dagegen von Anfang an, d. h. auch wenn der DOM-Baum noch nicht geladen wurde.)
  11. Alle Funktionen wurden mit Kommentaren im JSDoc-Format versehen.[1] Machen Sie das auch immer. Andere Entwickler und auch Sie selbst werden das schätzen, wenn sie bzw. Sie den Code zu einem späteren Zeitpunkt lesen und verstehen müssen. Das JSDoc-Format bring den Vorteil mit sich, dass Sie automatisch eine Schnittstellen-Dokumentation für Ihre Anwendung erstellen können

Wenn Sie jetzt Ihre Anwendung testen, hat sich nichts geändert. Der Grund ist wie bei der Datei main.css, dass die Datei index.html nichts davon weiß, das ihr dieser JavaScript-Code zugeordnet ist. Das muss ihr erst bekannt gegeben werden. Fügen Sie folgende Zeile in der Head-Bereich der index.html ein:

<script src="main.js" async></script>

Nun wird nicht nur die CSS-Datei, sondern auch die JavaScript-Datei geladen, bevor der Body-Bereich der index.html eingelesen wird. Die Angabe von async bewirkt Folgendes: Das Rendering der Seite wird durch das Laden des Skripts nicht unterbrochen. Das heißt, wenn ein langes Skript ohne Angabe von async geladen wird, kann sich der Seitenaufbau merklich verzögern. Mit Angabe dieser Option ist dies nicht der Fall.

Sobald das Skript geladen wurde, wird es ausgeführt. Zu diesem Zeitpunkt ist der Rendervorgang möglicherweise noch nicht abgeschlossen. Das heißt, man muss darauf achten, dass man von Skript aus nicht zu früh auf die Elemente des HTML-Dokuments zugreift. Aus diesem Grund wird die init-Funktion am Ende der Datei main.css nicht direkt mittels init() gestartet, sondern mittels eines Eventlisteners window.addEventListener('load', init).

Beachten Sie, dass folgender Code zwar SGML/XML-konform, aber in Standard-HTML5-Dokumenten nicht erlaubt ist:

<script src="main.js" async="async"/>

Der Schrägstrich am Ende des Tags ist auch (self closing tag) SGML/XML-, aber nicht HTML5-konform. Die Brwoser sind hinsichtlich der Einhaltung von Konfotmitätsregel allerdings nicht sonderlich pedantisch. Sie interpretieren auc nicht-konformen HTML-Code. Allerdings können dabei versciedene Brwoser durchaus zu verschiedenen Interpretationen kommen.

Die tieferen Gründe dafür werden sehr schön auf Stack Overflow beschrieben. (Ich verstehe es trotzdem nicht.)

Testen Sie Ihre Anwendung nochmals. Sie sollten jetzt Ihren Namen in das Textfeld eingeben und dann „Say hello“ klicken können. Das Ergebnis sollte sein, dass Sie von Ihrer Anwendung persönlich begrüßt werden. Wenn alles funktioniert, sollten Sie wieder committen.

Barrierefreiheit

Gemäß den „Richtlinien für barrierefreie Webinhalte“[2], Abschnitt 2.1 soll eine Web-Entwickler dafür Sorge tragen, dass „alle Funktionalitäten per Tastatur zugänglich“ sind ( “Make all functionality available from a keyboard. ”). Das ist bei unserer Web-Anwendung nicht in voller Schönheit der Fall. Der Cursor befindet sich wegen des Attributes „autofocus“ im Text-Input-Feld des HTML-Dokument bei Start der Anwendung automatisch an der richtigen Position. Man muss also den Cursor nicht erst mit Hilfe der Maus platzieren. Auch die Weiterschaltung mittels Tab-Taste funktioniert. Man kann zunächst seinen Namen eingeben, dann zweimal die Tab-Taste betätigen, um den Submit-Button zu aktivieren und dann Return drücken, um ein Klick-Event für diesen Button auszulösen.

Schöner wäre es jedoch, wenn man nach Eingabe des Namens gleich die Returntaste nutzen könnte, um die Eingabe abschließen zu können. Zurzeit passiert nichts, wenn Sie Ihren Namen eingeben und dann die Returntaste betätigen. Versuchen Sie es.

Damit begeben wir uns in die Untiefen der Benutzerinteraktion mit dem Browser...

Um diese Funktionalität zu realisieren, brauchen wir einen weiteren Eventlistener. Die Observer-Methode heiße sayHelloOnEnter. Diese muss wieder registriert werden. Die Tastaturereignisse werden vom window-Objekt gemeldet (und nicht etwa vom dokument-Objekt, da das Dokument nichts mit Tastatureingaben zu schaffen hat). Die Registrierung des Listeners erfolgt wie üblich mit Hilfe der addEventListener-Methode

window.addEventListener('keydown', sayHelloOnEnter);

Dieser Befehl kann sowohl innerhalb der init-Funktion, als auch außerhalb stehen (da das window-Objekt von Anfang an existiert). Man sollte es in den Rumpf der init-Funktion einfügen. Die Anzahl der globalen Befehle sollte so gering wie möglich gehalten werden. (Dieser Punkt wird später noch wesentlich genauer behandelt.)

Die (in diesem Teil des Tutoriums noch globale) Funktion sayHelloOnEnter wird folgendermaßen definiert:

function sayHelloOnEnter(p_event)
{ if (p_event.key === 'Enter')  // The enter key has been pressed.
  { // Prevent other handlers from handling the keypress event. 
    p_event.preventDefault();
    p_event.stopPropagation();

    // If the input text field or the submit button is active, say hello.
    if (document.activeElement === document.getElementById('input_name') ||
        document.activeElement === document.getElementById('button_submit')
       )
    { sayHello(); }

    // If the reset button is active, clear the input text field.
    else if (document.activeElement === document.getElementById('button_reset'))
    { document.getElementById('input_name').value = ''; }
  }
}

Man kann die Tests noch deutlich vereinfachen, da p_event im Attribut target das HTML-Element enthält, das beim Tastendruck aktiv war. Außerdem löst der aktive Buttons von aktuellen Browsern von sich aus beim Betätigen der Entertaste einen Click-Event aus. Das heißt, wir brauchen die Reaktion auf den Druck der Entertaste für die Buttons gar nicht zu implementieren.

function sayHelloOnEnter(p_event)
{ // If the enter key has been pressed while the focus is on text input field, say hello.
  if (p_event.key === 'Enter' && p_event.target.id === 'input_name')  
  { // Prevent other handlers from handling the keypress event. 
    p_event.preventDefault();
    p_event.stopPropagation();
    sayHello(); 
  }   
}

Fünf Dinge fallen bei dieser Definition auf:

  1. Die Funktion hat einen Parameter namens p_event (Parameternamen können beliebig gewählt werden. Ich beginne Parameternamen stets mit einem p_, um rein optisch klar zu machen, dass es sich um einen Parameter handelt; siehe Web-Programmierung: Style Guide). In diesem Parameter übermittelt das window-Objekt dem Eventhandler nähere Informationen zum aktuellen Tastaturereignis: welche Taste gedrückt wurde, ob die Shift-Taste dabei gehalten wurde etc. Details finden sich in der ausgezeichneten MDN-JavaScript-Dokumentation.
  2. Im Funktionsrumpf wird getestet, ob die Returntaste gedrückt wurde. Falls das Ergebnis des Test false lautetet, macht sie nichts (da das Zeichen einfach ins Textfeld eingefügt werden soll), anderenfalls ruft sie die Funktion sayHello auf, um die Eingabe abzuschließen.
  3. Die Gleichheit wird mit „===“ anstelle von „==“ getestet. Bei dem ersten Test handelt es sich um einen strikten Gleichheitstest. Der zweite Test liefert dagegen manchmal ziemlich eigenwillige Ereignisse. So wird beispielsweise bei den Tests „0 == ''“ „0 == '0'“ jeweils true als Ereignis ausgegeben. Der strikte Gleichheitstest liefert dagegen das erwartete Ergebnis false.
  4. Die Bedeutung der Enter-Taste muss abhängig vom Fokus verändert werden, da dieser mittels der Tab-Taste geändert werden kann. Wenn der Fokus auf dem Texteingabefeld oder dem Submit-Button liegt, soll der Benutzer gegrüßt werden. Liegt er dagegen auf der Reset-Taste, wird das Eingabefeld geleert. Bei den beiden Buttons ist dies das Defaultverhalten, für das Textfeld muss es implementiert werden.
  5. Im Code wird p_event.key === 'Enter' anstelle von p_event.keyCode === 13 verwendet. Die letztere Methode gilt als deprecated, da sie die Position der gedrückten Taste auf der Tastatur ausgibt. Diese Position ist jedoch sprachabhängig. Beispielsweise hat die Z-Taste auf einer amerkikansischen Tastatur eine andere Position als auf einer deutschen. Mit der folgenden JSFiddle-Anwendung können Sie die Unterschiede zwischen p_event.key, p_event.code und p_event.keyCode untersuchen. Öffnen Sie die Anwendung in verschiedenen Browsern und drücken Sie unterschiedliche Tasten (z. B. auch im Nummernblock.

So weit so gut. Aber wenn man sayHelloOnEnter so definiert, dass sie bei einem Druck der Returntaste lediglich die Funktion sayHello aufruft oder das Eingabefeld löscht, stellt man eventuell fest, dass ein Betätigen der Returntaste nicht wie gewünscht funktioniert. Das Problem ist, dass der Browser Tastatur-Ereignisse bereits anderweitig verarbeitet. Je nach Tastendruck werden Textfelder befüllt, Formularelemente aktiviert oder irgendwelche Spezialfunktionen ausgeführt. Man muss dem JavaScript-Interpreter klar machen, dass er dies im Falle der Returntaste nicht machen soll. Das ist Aufgabe der beiden folgenden Befehle, die in den Eventhandler eingefügt wurden:

p_event.preventDefault();
p_event.stopPropagation();

Der erste verlangt, dass bei Drücken dieser Taste keine Default-Aktionen ausgeführt werden sollen und der zweite bewirkt, dass das Ereignis nicht auch noch von irgendwelchen anderen Eventhandlern behandelt wird. Fügen Sie also die beiden Befehle noch an den Anfang des Rumpfes der If-Anweisung in sayHelloOnEnter ein.

Fazit: Bis eine Web-Anwendung wirklich so läuft, wie man sich das vorstellt, vergeht viel Zeit. Gerade bei mobilen End-Geräten hilft nur testen, testen, testen ...

Vergessen Sie nicht, Ihr aktuelles Projekt auf dem Git-Server zu speichern, sobald alles funktioniert.

Fortsetzung des Tutoriums

Sie sollten nun Teil 4 des Tutoriums bearbeiten.

Quellen

  1. Kowarschick (WebProg): Wolfgang Kowarschick; Vorlesung „Web-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2024; Quellengüte: 3 (Vorlesung)