JavaScript Einführung

Übungsblätter: javascript_forms.pdf

Arbeitsumgebung und vorgeschlagene Tools

Da HTML ein Standard des W3C ist, gibt es viele verschiedene Tools, um Webseiten und Bildinhalte zu erstellen und diese anzuzeigen. Aus technischen und menschlichen Gründen sind Open Source Programme für die Verwendung im Unterricht ideal. Da der Kurs leider auf Microsoft Windows (und nicht auf einer von mir bevorzugten Linuxdistribution) basiert schlage ich folgende Tools vor:

  • Texteditor: Notepad++
    • Einstieg ist einfach, daher geht keine Zeit für das Erlernen verloren; dennoch sehr mächtig und flexibel
    • auch für CSS, Javascript, PHP und viele andere Sprachen geeignet
    • [Strg]+[Alt]+[Shift]+X lädt die aktuell editierte Seite in Firefox
    • Codevervollständigung mit [Strg]+[Space]
    • Wortvervollständigung mit [Strg]+[Shift]+[Space]
    • Unterstützt Zen Coding
  • Webbrowser: Firefox oder Chromium
    • schnell, einfach zu bedienen, relativ sicher und sehr gute Implementierung der W3C Standards
    • schnelle Source-Ansicht per [Strg]+[u]
    • kann den Source einzelner Frames anzeigen
    • Personal Bookmark leiste zum schnellen Öffnen von Seiten
    • Bookmark drag & drop
    • zahllose Erweiterungen, die den HTML Code einer Seite lokal auf Fehler überprüfen und beim Entwickeln helfen
    • IE und andere Browser sollten aber auch zum Testen der Seiten verwendet werden
  • FTP Client: Filezilla
    • kann neben FTP auch sichere Übertragungsprotokolle wie SFTP
    • der beste mir bekannte FTP Client für Microsoft Windows
  • Bildbearbeitungsprogramm: GIMP (GNU Image Manipulation Program)
    • sehr mächtiges Bildbearbeitungsprogramm, dass vor allem fuer die Aufbereitung von Bildern für Websteiten, aber auch für den Druck sehr gut geeignet ist
    • gutes Tool zur Farbauswahl fü HTML und CSS

Alle Programme stehen für mehrere Betriebssysteme (Windows, Linux,...) zur Verfügung. Um einen reibungslosen Ablauf zu gewährleisten, steht während dem Kurs ein Linuxrechner mit dem Apache Webserver und PHP und MySql Unterstützung zur Verfügung. Weiters ist auf dem Rechner ein FTP und ein SSH Server installiert, um beim Upload der Dateien keine Zeit zu verlieren.

Verbindung zu HTML

JavaScript kann entweder in den HTML <head>, den Body oder in eine eigene Datei eingefügt werden. Im letzten Fall muss selbstverständlich ein Verweis auf diese Datei in den HTML <head> geschrieben werden. Sowohl im Head als auch im Body verwendet man das <script> Tag, um den Code einzufügen.

<script type="text/javascript">
    //<![CDATA[
    ...
    //]]>
</script>

Der CDATA (character data) Eintrag ist eine moderne Art, Browsern mitzuteilen, dass sich zwischen seinem Anfang (//<![CDATA[) und seinem Ende (//]]>) kein XML befindet. Da dies nur für XML relevant ist, ist CDATA bei HTML 4.01 bzw. HTML 5 nicht zu verwenden. Die Schrägstriche beginnen JavaScript Kommentare. In der Regel ist es aber empfehlenswert, JavaScript in eigene Dateien zu schreiben und mittels

<script type="text/javascript" src="path/file.js" />

in den HTML Code einzubinden. Funktionsaufrufe können weiters direkt an HTML Tags gebunden werden, indem man Ereignisse wie load verwendet. Ereignisse und JavaScript sollten generell aber nicht direkt in den HTML Code eingebunden werden, sondern in einer separaten JavaScript Datei definiert und mit den Tags verbunden werden, um die Dokumentstruktur vom Benehmen der Seite zu trennen. Dies ist eines der Grundprinzipien modernen Webdesigns.

Bedingte Ausführung und Schleifen

Im folgenden wird nur ganz kurz die Syntax der einzelnen Schleifen dargestellt, auf eine Erklärung wird verzichtet. Falls der Code nicht klar sein sollte, empfehle ich, damit zu experimentieren, da dies die beste Art ist, Programmieren zu lernen.

if (expression) {
  ...
} else if (expression) {
  ...
} else {
  ...
}
var myVar = condition & value_if_true : value_if_false;
switch (variable) {
  case label1:
    ...
    [break;]
  case label2:
    ...
    [break;]
  case label3, label4, label5:
    ...
    [break;]
  default:
    ...
}
while (condition) {
  ...
}
do {
  ...
} while (condition)
for (initialization; condition; modification) {
  ...
}
for (var key in array) {  // also works for assoc. arrays
  // use array[key]
  ...
}
for (var elem of array) {  // does not work for assoc. arrays
  // use elem directly
  ...
}

Funktionen

Funktionen sind nützlich, um den Code zu strukturieren, lesbarer zu machen und um ihn besser immer und immer wieder verwenden zu können. Eine Funktion sollte genau eine Aufgabe erfüllen - nicht mehr und nicht weniger und ihr Name sollte diese Aufgabe beschreiben - daher ist es oft sinnvoll, den Namen der Funktion mit einem Verb beginnen zu lassen. Wenn die Funktion mehr als eine Sache macht und man daher Schwierigkeiten hat, die Funktion treffend zu benennen, so ist das ein gutes Anzeichen dafür, dass man die Funktion wohl besser in mehrere Teilfunktionen aufteilen sollte. Die Syntax einer "normalen" (statischen) Funktion sieht so aus

function doStuff([arg1, arg2, ...]) {
  ...
  [return something;]
  ...
}

Ein Rückgabewert kann mit return erstellt werden, welches die Funktion auch sofort beendet, wenn es aufgerufen wird. Die Verwendung von return ist nicht verpflichtend, aber in den meisten Fällen sinnvoll, denn nur so kann man das Ergebnis einer Funktion in einer Variable speichern und dadurch später weiterverwenden.

var result = doStuff(myArg);

Funktionen können aber auch als Objekte (dynamisch) erstellt werden. Ein kurzes Syntaxbeispiel dazu findet sich bei der Beschreibung der JavaScript Objekten. Eine weitere Art Funktionen zu deklarieren ist folgende

var doStuff = function([arg1, arg2,...]) {
  ...
}
Ein konkretes (aber nicht sehr sinnvolles) Beispiel dafür ist
var sayHello = function(x) {
  alert(x);
}
sayHello("Hello Function!");

Es ist auch möglich der Funktion einen Namen zu geben, der aber lediglich innerhalb der Funktion bekannt ist, also nur für Rekursionen (die Funktion ruft sich selbst auf) sinnvoll ist. Die Syntax sieht dann wie folgt aus

var doStuff = function calculateStuff([arg1, arg2,...]) {
  ...
  calculateStuff([arg1, arg2,...]);
  ...
}

Es ist auch möglich, eine Funktion aus Strings herzustellen.

var doStuff = new Function(["arg1", "arg2"], "alert('arg1 = ' + arg1);");

Objekte

Im folgenden ist anhand von Code (sehr) kurz beschrieben, wie man in JavaScript Objekte instanziert und verwendet. Nummer, String und Boolean Objekte sind nicht erwähnt, weil diese in der Regel implizit verwendet werden, jedoch werden einige der vorhandenen Eigenschaften und Methoden auch aufgelisted (myString ist eine variable, die eine Zeichenkette enthält). Methoden, die man aus verschiedenen Gründen meiner Meinung nach nicht verwenden sollte, sind nicht erwähnt (wobei auch viele nützliche Methoden weggelassen werden, da die folgende Liste nur beispielhaft ist). Auf eine Erklärung regulärer Ausdrücke (regular expressions) wird aufgrund ihrer Komplexität vollständig verzichtet (siehe Links oder bitte eine Suchmaschine verwenden...).

myString.length;
myString.charAt(position); myString.charCodeAt(position);
myString.indexOf(substring); myString.lastIndexOf(substring);
myString.split(separator, maxNum);
myString.slice(beginning, end);
myString.match(regExp); myString.replace(regExp); myString.search(regExp);
myString.toLowerCase(); myString.toUpperCase();

var pattern = new RegExp(regExpString);

var myDate = new Date();
myDate.getFullYear();
myDate.getMonth(); // starting with 0 for January
myDate.getDate(); // day of month
myDate.getDay(); // day of week; starting with 0 for Sunday
myDate.getHours();
myDate.getMinutes();
myDate.getSeconds();
myDate.getMilliseconds();
myDate.setXXX(arg); // corresponding set method

Math.E; Math.LN10; Math.LN2; Math.LOG2E; Math.LOG10E; Math.PI;
Math.SQRT1_2; Math.SQRT2;
Math.sin(x); Math.asin(x);
Math.cos(x); Math.acos(x);
Math.tan(x); Math.atan(x);
Math.floor(number); Math.ceil(number);
Math.pow(base, exponent);
Math.min(num1, num2,...); Math.max(num1, num2,...);
Math.random(); // returns a random number ≥0 and <1

var myArray = new Array(); // alt. var myArray = [value1, value2,...];
myArray.length;
myArray.splice(index,howMany,[newValue1,newValue2,...]); // cuts and returns elements
myArray.slice(begin[,end]); // returns elements
Array.join(separator) // joins array into a string
myArray.push(element[,element2,...]); myArray.pop();
myArray.unshift(element[,element2,...]); myArray.shift();
myArray.concat(otherArray);
myArray.reverse();

Die Definition von Objekten ist in JavaScript für Webprogrammierer selten notwendig, da dies hauptsächlich in komplexeren Bibliotheken wie zum Beispiel JQuery gemacht wird. Dennoch ist im Folgenden kurz aufgezeigt, wie eigene JavaScript Objekte definiert werden können.

// JavaScript object instances are associative arrays
var associativeArray = new Object();
associativeArray["index1"] = "contents1";
associativeArray.index2 = "contents2";
associativeArray.memberObj = new Object();
associativeArray.memberObj.name = "a name";
associativeArray.memberObj.street = "street address";
associativeArray.index1; // accesses the first element

// an alternative (less verbose) way to define the above using JSON
index2 = "bob";
var myObj = {
  "index1": "contents1",
  "index2": "contents2",
  "memberObj": {
    "name": "a name",
    "street": "street address"
  }
};

// an extensive example providing class methods and using prototype
function Teacher(name)
{
  // initialize the member variables for this instance
  this.name = name;

  // initialize the member function references 
  // for the class prototype
  if (typeof(_teacher_prototype_called) == 'undefined')
  {
     _teacher_prototype_called = true;
     Teacher.prototype.getName = getName;
     Teacher.prototype.setName = setName;
  }

  // define a Teacher's methods
  function getName()
  {
     return this.name;
  }

  function setName(name)
  {
     this.name = name;
  }
}
function test_teacher()
{
  var teacher = new Teacher('anna');
  window.alert('Teacher: ' + teacher.getName());
}

Events

Events (Ereignisse) treten ein, wenn ein Benutzer eine bestimmte Aktion durchführt. Eine solche Aktion kann das klicken auf eine Schaltfläche sein, das Schreiben in einem Textfeld, das Laden der Seite und noch viel mehr. Es gibt verschiedene Eventmodelle wovon hier nur das derzeit aktuelllste von DOM2 beschrieben wird. Hauptgrund dafür ist, dass JavaScript so gut wie möglich vom HTML Code getrennt sein sollte. Generell kann einem Objekt ein Eventhandler (eine Funktion, die ausgeführt wird, wenn das Event eintritt) wie folgt zugewießen werden

object.addEventListener("event", eventFunction, boolean);

Das Argument event kann (vom Objekt abhängig) eines der folgenden sein

abort, blur, focus, change, click, doubleclick, contextmenu, error,
keydown, keyup, keypress, load, unload, mousedown, mouseup, mouseover,
mouseout, mousemove, reset, resize, select, scroll, submit

wovon blur und submit imho die wichtigsten (für Formularvalidierung) sind. Das zweite Argument ist die Funktion, die bei eintreten des Events gestartet werden soll. Diese Funktion bekommt als Argument stets das Eventobjekt als Argument. Das dritte Argument bestimmt, in welcher Reihenfolge Events ausgelöst werden sollen: true veranlasst eine Ausführung von "Aussen nach Innen" (also zuerst für das Dokument, dann für ein darin enthaltenes Element, dann für ein Unterelement usw.), false bewirkt eine Ausführung von "Innen nach Aussen". Ein konkretes Beispiel sieht zum so aus

document.getElementById("myElem").addEventListener('submit', myHandler, true);

Da Microsoft (wie so oft) quasi der einzige Browseranbieter ist (alle IE bis inklusive Version 7), dessen Eventmodell nicht mit den Standarts des W3C kompatibel ist, ist es empfehlenswert folgendes Konstrukt zum umgehen von Inkompatibilitäten zu verwenden

if (myObject.addEventListener) {
  myObject.addEventListener("event", eventFunction, boolean);
} else if (myObject.attachEvent) {
  myObject.attachEvent("onevent", eventFunction);
} else if (myObject.onevent) {
  myObject.onevent = eventFunction;
}

Das Eventobjekt

Die Art, wie Eventobjekte an die behandelnden Funktionen übergeben werden, ist leider auch nicht für alle Browser gleich. Genausowenig sind die Eigentschaften dieser Objekte konsistent zwischen verschiedenen Browsern. Folgend aber eine kurze Liste der konsistenen Eigentschaften

altKey, clientX, clientY, ctrlKey, keyCode, screenX, screenY, shiftKey, type

Um Inkonsistenzen zu umgehen hilft hier folgender Trick (der Variable wird die Eigenschaft als Resultat einer Abfrage zugewiesen)

var myProperty = myEvent.mozProperty ? myEvent.mozProperty : myEvent.ieProperty

JiT Validierung

Just in Time Validierung wird für Formulare verwendet, um sobald eine "fehlerhafte" Eingabe beendet ist, dem User so gut wie möglich dabei zu helfen, diese auszubessern. Es ist sehr empfehlenswert, diese Kontrolle gleich nach der Eingabe und nicht erst am Ende des Formulars durchzuführen, damit sich nicht zu viele (für den User frustrierende!) Fehler anhäufen. Besonders wichtig für Formulare ist dennoch das submit Event - es tritt ein, wenn die Formulardaten abgeschickt werden. Falls man dabei einen Fehler entdeckt, sollte das Senden des Formulars abgebrochen werden (sonst werden die bereits eingegebenen Daten beim Benutzer gelöscht). Auch dieses Abbrechen ist nicht konsistent zwischen Browsern

document.myForm.addEventListener("submit", myHandler, boolean);

if (submitEvent.cancelable){
  submitEvent.preventDefault();
} else if (submitEvent.cancelBubble != null) {
  submitEvent.cancelBubble = true;
}
return false;

Ein weiteres Problem bei der Validierung von Inhalten ist, dass zumindest Grundkenntnisse von regulären Ausdrüken nötig sind. Am besten ist Formularvalidierung aber anhand eines Beispiels zu demonstrieren: form_validate.html, form_validate.js. Bei der Erstellung der nötigen regulären Ausdrücke hilft mein regExpTester.

Rekursion

Rekursion ist ein Fachausdruck der beschreibt, dass sich eine Funktion immer wieder selbst aufruft. Dies sieht auf den ersten Blick danach aus, als wuerde das Programm nie von selber enden... und genauso soll es eventuell auch sein. Am besten stellt man sich dazu sein Betriebssystem oder das Lieblings-Schreibprogramm vor - hier wollen wir doch auch nicht, dass das Programm seine Ausfuehrung von selber abbricht (wobei in diesem Fall oft eine zu while (true) äquivalente Schleife zum Einsatz kommt). Für JavaScript sind Rekursionen zum Beispiel wichtig, wenn man eine Uhr auf einer Seite anzeigen will, weil die Funktion, die das Aktualisieren jede Sekunde erledigt, sich immer wieder (zeitverzögert) selbst aufrufen muss. Am Besten sei das an einem kleinen Beispiel demonstriert

function updateTime()
{
  date = new Date();
  var time = date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
  window.document.display.time.value = time;
  window.setTimeout('updateTime()', 1000);
}

Hier geschieht das rekursive Wiederaufrufen anhand der Funktion window.setTimeout('function(args)', time_in_ms). Eine Rekursion kann aber auch direkt erfolgen und durch eine spezielle Abbruchsbedingung von selbt zu einem Ende kommen - zum Beispiel fuer die Berechnung der Faktoriellen einer Zahl n. Die Faktorielle von n (n! geschrieben) ist das Produkt von 1 * 2 * 3 * ... * n. Zum Beispiel ist 4! = 1 * 2 * 3 * 4 = 24. Dies kann aber auch als 4! = 4 * 3 * 2 * 1 = 24 geschrieben werden. Eine rekursive JavaScript Funktion, die dies berechnet, sieht so aus

function fact(n)
{
  if (n == 0){
    return 1;
  }
  return fact(n-1)
}

DOM (Document Object Model)

Ein XHTML Dokument ist aus einer Hierarchie von Knoten (nodes) aufgebaut. Das Dokument selbst und seine Knoten sind Objekte, welche mit Hilfe von JavaScript erstellt, verändert und entfernt werden können. Das DOM bietet eine standardisierte Möglichkeit, jegliches XML Dokument (nicht nur XHTML) zu bearbeiten - und zwar nicht nur mit JavaScript, sondern auch mit jeder anderen Programmiersprache, die das DOM implementiert. Es folgt eine kurze Liste der nützlichsten Funktionen, welche vom DOM Core (das DOM Core funktioniert im Gegensatz zum HTML DOM mit jedem XML Dokument) zur Verfügung gestellt werden.

reference = element.appendChild(new_child);
fügt einen neuen Knoten am Ende von element ein und gibt eine Referenz zu diesem zurück
oder verschiebt einen bereits im Dokument vorhandenen Knoten an das Ende von element
reference = node.cloneNode(include_child_nodes);
erstellt eine Kopie eines vorhandenen Knoten und gibt eine Referenz zu diesem zurück
reference = document.createElement(tag_name);
erstellt einen neuen Knoten (zum Beispiel einen neuen Absatz) und gibt eine Referenz zu diesem zurück
nodeType = 1
reference = document.createTextNode(text);
erstellt einen neuen Knoten (einen Text) und gibt eine Referenz zu diesem zurück
nodeType = 3
attribute_value = element.getAttribute(attribute_name);
gibt den Wert des angegebenen Attributs zurück
war dieses Attribut nicht definiert, so wird ein leerer String zurückgegeben
element = document.getElementById(id);
gibt den Knoten mit der angegebenen ID zurück
existiert kein solcher Knoten, so wird null zurückgegeben
elements = element.getElementsByClassName(class_name);
gibt ein Liste mit allen Elementen mit dem angegebenen class_name zurück (Achtung: dies ist keine Standard-DOM Funktion)
elements = element.getElementsByTagName(tag_name);
gibt ein Liste mit allen Elementen mit dem angegebenen tag_name zurück
wird häufig in Kombination mit einer for-Schleife verwendet:
for (var i=0; i<elements.length; i++) { ... }
reference = element.insertBefore(new_node, reference_node);
fügt einen neuen Knoten vor reference_node ein und gibt eine Referenz zu diesem zurück
oder verschiebt einen bereits im Dokument vorhandenen Knoten an das Ende von reference_node
reference = element.removeChild(node);
entfernt einen Knoten aus dem Dokument und gibt eine Referenz zu diesem zurück
hat man keine Referenz zu element, so kann man node.parentNode verwenden
reference = element.replaceChild(new_child, old_child);
ersetzt einen Knoten mit einem anderen und gibt eine Refernz zu dem alten Knoten zurück
war der neue Knoten bereits im Dokument vorhanden, wird er aus seiner ursprünglichen Position entfernt
element.setAttribute(attribute_name, attribute_value);
fügt einem Knoten ein neues Attribut hinzu oder ändert ein bereits vorhandenes Attribut

Ebenso sind die folgenden Eigenschaften von DOM Core Elementen besonders interessant

node.childNodes
Liste (Array) der childNodes
node.nextSibling
node.nodeType
1 == ELEMENT_NODE
2 == ATTRIBUTE_NODE
3 == TEXT_NODE
4 == CDATA_SECTION_NODE
die restlichen 8 Typen sind auf https://developer.mozilla.org/ erklärt
node.parentNode
node.previousSibling

Die konkrete Kenntniss aller verschiedener Eigenschaften ist aber wenig sinnvoll - verwendet man ein vernünftiges Tool zur Webentwicklung wie zum Beispiel Opera Dragonfly, so bekommt man kinderleicht eine Übersicht über alle Eigenschaften eines Knotens. Ein Zuckerl am Rande: Dragonfly ist Open Source!

Link kurze Beschreibung
http://en.wikipedia.org/wiki/JavaScript_syntax Exzellenter Überblick über die Syntax von JavaScript (Englisch!)
http://regexlib.com/ Sehr gute Seite, die beim Erstellen von regulären Ausdrücken (nicht nur für JavaScript) hilft (Englisch!)
http://www.free-webhosts.com/ Vergleich von Webhosting Services (Englisch!)
[Valid XHTML 1.1!]