Javascript / K345.dElement()

K345.dElement() ist ein Funktion, mit deren Hilfe das Erzeugen aufwendiger HTML-Element-Strukturen vereinfacht werden soll.

Einführung

K345.dElement() ist eine Funktion, mit deren Hilfe das Erzeugen aufwendiger HTML-Element-Strukturen vereinfacht werden soll. Dabei werden diverse Unzulänglichkeiten – insbesondere älterer Browser – als auch einige Javascript-Eigenheiten wie z.B. die erforderliche Änderung bestimmter Attribut-Namen automatisch berücksichtigt.

Node|Fragment reference = K345.dElement(Object declaration);

Die Funktion dAppend() übernimmt zusätzlich noch das automatische Einhängen der erzeugten Elemente an ein vorhandenes Element.

Node|Fragment reference = K345.dAppend(
    String|Node target id|reference, Object declaration [, Const mode_flag]
);

dElement() / dAppend() gibt einen DOM-Element-Baum oder ein documentFragment mit den erzeugten Elementen zurück. Dieser Wert kann mit DOM-Methoden (wie zum Beispiel appendChild, insertBefore oder ähnliches) direkt verwendet werden.

WarnungdElement überprüft nicht[1] die Sinnhaftigkeit der Elementverschachtelung; unsinniger Code wie <span><ul><p> (hier als HTML-Repräsentation) ist daher möglich. Wie der mit dElement erzeugte ungültige Element-Baum verarbeitet wird, hängt allein vom verwendeten Browser ab.

[1] Wenn Elementen, die keinen Inhalt haben dürfen (z.B. img, input, hr, br, meta,…) ein Inhalt zugewiesen wird, wirft dElement einen Fehler

Hintergrund

Es gibt verschiedene Möglichkeiten, Elemente und Elementstrukturen zu erzeugen und in ein HTML-Dokument einzufügen; alle mit Vor- und Nachteilen.

innerHTML

Die (vor allem bei Anfängern) wahrscheinlich bekannteste Methode ist, eine HTML-Zeichenkette zu erzeugen und diese dann einem Element zuzuweisen. Dies ist für einfache Elementstrukturen schnell erzeugt und relativ übersichtlich. Sobald die zu erzeugenden Strukturen jedoch komplexer werden, ergeben sich diverse Probleme:

Mögliche Probleme:
  • Man muß sich selbst um korrekte Elementschreibweise (schließende Tags, optionale schließende Tags, leere Elemente; je nach gewähltem DOCTYPE) kümmern.
  • mit zunehmender Komplexität wird es immer schwieriger, die öffnenden / schließenden Element-Tags richtig zuzuordnen, was insbesondere bei nachträglichem Ändern dazu führen kann, daß an der falschen Stelle geändert wird.
  • Beim Einfügen variabler Werte muß die Zeichenkette vielfach unterbrochen und mit dem „+“-Operator wieder zusammengefügt werden.
  • Es ergeben sich Probleme, wenn mehrere Typen Anführungszeichen benötigt werden, hier ist dann korrektes Escaping notwendig, um Syntax-Fehler zu vermeiden.
  • Zum Anhängen an bestehende Dokumente oder größere Dokument-Teile (bspw. document.body.innerHTML += '<div>…</div>') ist innerHTML sehr ungeeignet, da der gesamte Element-Baum zu einer Zeichenkette serialisiert werden muß, dann mit dem neuen Code zusammegefügt und danach wieder zu einem Elementbaum zurückgewandelt werden muß.
  • Beim Serialisieren und Zurückwandeln können Event-Handler unwirksam werden, die bspw. mit addEventListener() zugewiesen wurden, da alle Elementreferenzen neu erzeugt werden und somit die ursprünglichen Referenzen der Handler ungültig sind.

DOM-Methoden

Bei der Erzeugung von Elementen mit DOM-Methoden (createElement, createTextNode, appendChild, insertBefore u.v.m) gibt es einige Dinge zu beachten sowie viele Fallstricke, die in einigen Browsern (vor allem ältere Internet-Explorer) dazu führen können, daß die Erzeugung fehlschlägt und / oder nicht das gewünschte Ergebnis erzeugt wird.

Mögliche Probleme:
  • Es gibt zwischen verschiedenen Browsern viele Inkompatibilitäten und häßliche Bugs zu beachten.
  • Bei komplexeren Strukturen fallen viele Variablen und viel Schreibarbeit an.
  • Man muß genau aufpassen, welches erzeugte Element an welches Andere angehängt werden muß.
  • Die Syntax einiger Methoden ist relativ umständlich, beispielsweise bei insertBefore().
  • Einige HTML-Attributnamen erfordern bei direkter Zuweisung (d.h. wenn nicht setAttribute() genutzt wird) einen alternativen Namen oder in bestimmten Browsern eine bestimmte Schreibweise des Names (camelCase)

dElement / dAppend

Erzeugt einen Element-Baum unter bevorzugter Verwendung der DOM-Methoden, wobei diverse Browser-Bugs und sonstige Probleme berücksichtigt werden (wenn auch bei Weitem nicht alle; das kann ich im Alleingang nicht bewältigen) und auch Attribut-Namen automatisch angepasst werden. Alle Attribut-Namen können direkt als Schlüsselwort in der Deklaration verwendet werden (siehe auch Abschnitt Syntax).

Mögliche Probleme:
  • Verschachtelte Syntax kann gegebenenfalls verwirrend wirken (kann durch Aufteilen in verschiedene Teilobjekte vermieden / gemindert werden).
  • Aufbau von Text, bei dem (mehrfach) einzelne Wörter / Sätze / Abschnitte in Elemente eingeschlossen werden sollen, ist aufwendig. (hier ist es eventuell einfacher, über das spezielle Schlüsselwort „html“ entsprechendes Markup als Zeichenkette zu übergeben. Überholt. Einfachere Verwendung ist jetzt durch Übergabe als Array oder durch mehrere text_… Schlüsselwörter möglich (siehe auch Multi-Eigenschaft).
  • dElement muß bei Änderungen / Erweiterungen der Spezifikationen sowie eventueller Inkompatibilitäten aktualisiert werden (wie fast jede externe Ressource).

Ein praktischer Vergleich

Nun der Unterschied zwischen den Methoden anhand eines Beispiels, bei dem eine relativ einfache Element-Struktur erzeugt wird, welche variable Werte enthält.

Vorgegebene Variablen
var link_href = "http://example.org/home.html",
	img_w = 200,
	img_src = "/res/arrow.gif";

Zur Erzeugung von folgenden HTML-Elementen und Einhängen an ein (im fiktiven Element-Baum bereits vorhandenes) Element mit der ID „my_element“ …

Das zu erzeugende HTML
<!-- Link mit Bild und Text -->
<a href="http://example.org/home.html" title="Link to new page">
	<span><img src="/res/arrow.gif" width="200" alt="an arrow">some text</span>
</a>

… wäre bei Verwendung der DOM-Methoden folgendes Javascript zu schreiben:

Erzeugung mit DOM-Methoden
// link
var my_link = document.createElement("a");
	my_link.href = link_href;
	my_link.title = "Link to new page";
// bild
var my_img = document.createElement("img");
	my_img.src = img_src;
	my_img.width = img_w;
	my_img.alt = "an arrow";
// text
var my_span = document.createElement("span");
var my_text = document.createTextNode("some text");
// einhängen
	my_span.appendChild(my_img);
	my_span.appendChild(my_text);
	my_link.appendChild(my_span);
document.getElementById("my_element").appendChild(my_link);

und bei Verwendung von innerHTML:

Erzeugung mit innerHTML
document.getElementById("my_element").innerHTML = '<a href="' + link_href +
	'" title="Link to new page"><span><img src="' + img_src + '" width="' +
	img_w + '" alt="an arrow">some text</span></a>';

Dies ist bei einer so geringen Menge erzeugter Elemente zwar noch halbwegs überschaubar, allerdings muß man schon hier genau aufpassen, um die Elemente / Texte korrekt wie gewünscht ineinander zu verschachteln bzw. bei innerHTML die verschiedenen Anführungszeichen nicht zu verwechseln und keine Maskierung oder schließende Tags zu vergessen.

Daher habe ich eine Funktion entwickelt, die ein strukturiert geschriebenes, verschachteltes Objekt-Literal in einen DOM-Elementbaum umwandelt. Das obige Beispiel könnte[2] folgendermaßen geschrieben werden:

Element-Erzeugung mit dElement
// Beispiel in kompakter Schreibweise
var def = {
	element: "a", href: link_href, title: "link to new page",
	child: { element: "span", child: [
		{ element: "img", src: img_src, width: img_w, alt: "an arrow" },
		{ text: "some text" }
	]}
};
var my_link = K345.dElement(def);
document.getElementById("my_element").appendChild(my_link);

Zur Verdeutlichung des Aufbaus der Deklarations-Syntax folgt nun der gleiche Code in einer besonders anschaulichen Schreibweise[2].

Eine strukturiertere Schreibweise - kommentiert
var def = { // Start der Deklaration des "a"_Elements
	element: "a", // Schlüsselwort "element" definiert den Elementnamen
	href: link_href, // eine Variable
	title: "Link to new page",
	child: { // "a" soll nur ein Kind-Element haben, daher neues Objekt-Literal
		element: "span",
		child: [ // "span" hat zwei gleichberechtigte Kind-Elemente, daher Array-Literal
			{ // erstes Kind-Element: Objekt-Literal für "img"
				element: "img",
				src: img_src, // eine Variable
				width: img_w, // eine Variable
				alt: "an arrow"
			},
			{ // zweites Kind-Element: Objekt-Literal für den Text
				text: "some text"
			}
		] // Beendet Deklaration der Kind-Elemente von "span"
	} // Beendet Deklaration des Kind_Elements von "a"
}; // Ende der Deklaration von "a"
var my_link = K345.dElement(def); // my_link enthält Referenz auf "a"-Element mit allen Kindern
document.getElementById("my_element").appendChild(my_link);
 
Eine strukturiertere Schreibweise
var def = {
	element: "a",
	href: link_href,
	title: "Link to new page",
	child: {
		element: "span",
		child: [
			{
				element: "img",
				src: img_src,
				width: img_w,
				alt: "an arrow"
			},
			{
				text: "some text"
			}
		]
	}
};
var my_link = K345.dElement(def);
document.getElementById("my_element").appendChild(my_link);
Alternative Schreibweisen
// mit multi-Schlüsselwort 'child_*'
var def = {
	element: "a", href: link_href, title: "link to new page",
	child: { element: "span",
		child_arrowimg: { element: "img", src: img_src, width: img_w, alt: "an arrow" },
		child_mytext: "some text"
	}
};
var my_link = K345.dElement(def);
document.getElementById("my_element").appendChild(my_link);

Um eine verschachtelte Struktur zu vereinfachen und / oder die Anzahl Verschachtelungsebenen zu verringern, kann man die Deklaration auch aus mehreren Teilobjekten erstellen. (das ist für dieses konkrete Beispiel eigentlich übertrieben aufwendig und eher unübersichtlich; es soll nur das Prinzip darstellen)

Schreibweise in Teilobjekten
var def_img = { // Deklaration für Bild
	element: "img", src: img_src, width: img_w, alt: "an arrow"
};
var def_txt = {text: "some text"}; // Deklaration für Text

// Deklaration für span-Element - referenziert Bild und Text
var def_span = {
	element: 'span', child: [def_img, def_txt]
};
var def_a = { // Deklaration für a-Element - referenziert span-Element
	element: "a", href: link_href, title: "link to new page", child: def_span
};

var my_link = K345.dElement(def_a); // Alle Elemente erzeugen
document.getElementById("my_element").appendChild(my_link);

Bei einfachen Zeichenketten ist es nicht erforderlich, Objekte zu erzeugen {text: 'foo'}, {text: 'bar'}, sondern die Zeichenkette kann direkt übergeben werden und dElement erzeugt textNode. Gleiches gilt für mehrer Zeichenketten als Array ["foo", "bar", "baz"]

[2] Für Javascript ist die Verwendung von Einrückungen oder Zeilenumbrüchen eines Objekt-Literals unerheblich, man könnte auch alles ohne Umbrüche und Leerzeichen in eine einzige Zeile schreiben, solange die Regeln für Literal-Schreibweise beachtet werden

Syntax

[3] Eingeschlossen in die in Javascript zulässigen Zeichenketten-Begrenzungzeichen (' oder ")

Übersicht der speziellen Schlüsselwörter

element
Tag-Name des HTML-Elements, das erzeugt werden soll (Zeichenkette).
NEU: Erweiterte Syntax
text (multi, array)
Einfacher Text, der als textNode eingefügt wird, weitere Verschachtelungen werden ignoriert.
html (multi, array)
Eine Zeichenkette mit HTML-Markup, wie sie auch bei bspw. innerHTML verwendet wird.
child (multi, array)
Start einer neuen Teildeklaration, die Elemente werden als Kindelement(e) an das übergeordnete Element angehängt. Erwartet
  • eine Objekt-Deklaration
  • ein Array von Objekt-Deklarationen, wenn mehrere Elemente gleichberechtige Kind-Elemente sind
  • eine Elementreferenz oder einen DOM-Baum
  • eine Zeichenkette, wenn das einzige Kind-Element Text ist
event (multi, array)
Dem aktuell definierten Element ein Event hinzufügen. Erwartet ein Objekt mit den Eigenschaften „function“ und „arguments“ (alternativ: „func“ bzw. „args“)
clone, clonetop
Klont ein Node-Element. Bei Verwendung von „clonetop“ wird nur das aktuelle Element geklont, bei „clone“ das Element inclusive aller Kind-Elementen. Erwartet eine Element-Referenz.
Wichtig: Falls das zu klonenede Element eine »id« besitzt, muß diese beim Klon geändert werden, falls sowohl Original wie auch Klon gleichzeitig im Dokument verwendet werden. dElement ändert die id des Klons nicht!
attribute, attrib, attr (multi, array)
Erzwingt die Erzeugung eines Attributs via setAttribute() anstatt der direkten Zuweisung der Eigenschaft. Erwartet ein Objekt mit den Eigenschaften „name“ und „value“ oder ein Array von derartigen Objekten. Sollte nur bei Sonder- und Problemfällen[4] verwendet werden, stattdesssen sollte die gewünschte Eigenschaft direkt gesetzt werden.
elrefs
Sammelt alle erzeugten Element-Referenzen mit einer Eigenschaft „name“ oder „id“ in einem Objekt. Diese Element-Referenzen stehen als Eigenschaft des übergebenen Objekts sofort zur Verfügung und müssen bei Bedarf nicht erneut mit document.getElementById() ermittelt werden.
collect (multi)
Sammelt alle Element-Referenzen, die dieses Schlüsselwort enthalten, in einem oder mehreren Array(s) oder Objekt(en).
setif (multi)
Setzt eine Eigenschaft nur dann, wenn eine Bedingung erfüllt ist. Erwartet ein Objekt mit den Eigenschaften „name“, „value“ und „condition“ (alternativ: „cond“)
condition, cond
Einfügen von Elementen in Abhängigkeit einer Bedingung. Experimentell[5]
loop, loopdeep, loopstop NEU
erzeuge multiple ähnliche (Teil-)Deklarationen Experimentell[5]
comment, comm (multi, array)
Erzeugt Kommentar-Node aus einer Zeichenkette
init NEU
Die Eigenschaft „init“ verweist auf eine Funktions-Referenz. Diese Funktion wird ausgeführt, nachdem der Element-Baum erzeugt wurde. Experimentell [5]
style (array)
Setzt CSS-Eigenschaft(en). Erwartet eine Zeichenkette, in der die Eigenschaft-Wert-Paare wie in CSS selbst geschrieben werden. (also z.B. style: 'color:green; margin-left:3em').

[4] Es gibt [ältere] Browser, die bestimmte Eigenschaften nicht (erfolgreich) direkt setzen können, hier ist die Verwendung von „attribute“ zwingend erforderlich

[5] Diese Funktionalität befindet sich noch im Entwicklungs- und / oder Test-Stadium. Änderungen sind möglich. Verwendung kann unter Umständen zu Fehlern führen.

multi

Die als »multi« ausgewiesenen Eigenschaften dürfen in jeder Teil-Deklaration mehrfach verwendet werden. Da dies normalerweise in Javascript nicht möglich / erlaubt ist, weil vorhergehende Deklarationen entweder überschrieben werden oder ein schwerer Fehler auftritt, werden zusätzlich alle Schlüsselwörter erkannt, die mit dem Basis-Namen beginnen, gefolgt von einem Unterstrich »_« und einem beliebigen, aber eindeutigen alphanumerischer Wert (dieser dient lediglich dazu, den Namen eindeutig zu machen und wird ansonsten nicht weiter beachtet).

Beispiele: text_descript, child_list1, event_change4, attribute_whatever55

array

Die mit »array« gekennzeichneten Schlüsselwörter können zusätzlich zum jeweils erwarteten Wert (z.B. Zeichenkette, Objekt, Zahl) auch Arrays mit Werten des erwarteten Typs verarbeiten.

Erweiterte Syntax für Schlüsselwort „element“

Es ist möglich, Element-ID, Klassen-Namen – sowie bei Elementen, die dies unterstützen, auch Typ, Wert und Name – alternativ zu den entsprechenden Schlüsselwörtern und Attributen „id“, „className“, „type“, „value“, „name“ direkt im Schlüsselwort „element“ anzugeben. Hierzu wird eine von CSS inspirierte Syntax benutzt.

ZeichenBedeutungAttribut
#Element-IDid
.Element-KlasseclassName
@Element-Typtype
~Element-Namename
=Element-Wertvalue
$tag name des Elements

Um die einzelnen Bestandteile einfacher lesbar zu gestalten, ist es erlaubt, die einzelnen Bestandteile durch Leerzeichen zu trennen z.B. 'input @button .mystuff #myid'

Die Reihenfolge hinter dem Element-Namen ist beliebig (siehe Beispiel 3). Eine Id, ein Typ, ein Wert oder ein Name darf jeweils nur ein Mal verwendet werden, Klassen-Namen dürfen mehrfach verwendet werden.

Der tag name des Elements wird als erster Wert der Zeichenkette erwartet (siehe Beispiele) oder aber mit $ eingeleitet

Syntax
// ==== Beispiel 1: ID, Type und Name
// Standard
{element: 'div', id: 'foo-bar'};
{element: 'input', type: 'checkbox'};
{element: 'select', name: 'baz'};
// erweitete Syntax
{element: 'div#foo-bar'};
{element: 'input@checkbox'};
{element: 'select~baz'};

// ==== Beispiel 2: KLASSEN ====
// Standard
{element: 'div', className: 'bar'};
{element: 'div', className: 'bar baz'};
// erweitete Syntax
{element: 'div.bar'};
{element: 'div.bar.baz'};

// ==== Beispiel 3: KOMBINIERT ====
// Standard
{element: 'div', className: 'bar baz', id: 'foo'};
{element: 'input', type: 'radio', name: 'foo', id: 'bar'};
// erweitete Syntax
{element: 'div#foo.bar.baz'};
{element: 'input@radio#bar~foo'};
// Die Reihenfolge der Klassen und der Id ist unerheblich
{element: 'div.bar#foo.baz'};

Um die Verarbeitung möglichst einfach und schnell zu halten, ist der Bereich der erlaubten Zeichen eingeschränkt; es darf unter Anderem keines der jeweils anderen einleitenden Zeichen innerhalb eines Wertes verwendet werden, selbst wenn dies prinzipiell nach den HTML-Regeln erlaubt wäre (Ausnahme: value). In einem solchen Fall sollte weiterhin das jeweilige Schlüsselwort „id“, „className“, „type“, „name“ verwendet werden.

Wird ein Attribut sowohl als Schlüsselwort wie auch in der erweiterten Syntax angegeben, verwendet dElement nur den Wert der erweiterten Syntax (Ausnahme: Die Eigenschaft „className“ wird mit den Werten der erweiterten Syntax kombiniert).

Ein value-Attribut kann unter Anderem auch die Zeichen # . ~ @ enthalten. Eine sichere automatische Unterscheidung, ob beim Auftreten eines dieser Zeichen ein weiteres Attribut eingeleitet werden soll oder ob das Zeichen zum value-Attribut gehört, ist nicht möglich. Daher gibt es folgende Möglichkeiten:

Abgrenzung des value Wertes
// ans Ende
var el = K345.dElement({
	element: 'button @button .myClass =ein #hashtag und mail@example.org',
	text: 'Huhu'
});

// mit Steuerzeichen 'US'
var el = K345.dElement({
	element: 'button @button =ein #hashtag und mail@example.org\x1F .myClass',
	text: 'Huhu'
});
Erzeugtes Element in HTML-Schreibweise
<button type="button" class="myClass" value="ein #hashtag und mail@example.org">
	Huhu
</button>

.

HTML als Zeichenkette übergeben

Mit dem Schlüsselwort „html“ kann eine Zeichenkette mit HTML übergeben werden, die dann als Element(baum) eingefügt wird.

var new_el = K345.dElement({ element: 'p', html: '<span class="foobar">Hallo bla bla <u>Wichtig</u></span>' });

Aufgrund der zusätzlichen Verarbeitung (-> Geschwindigkeit) sowie diverser Browser-Bugs (siehe unten) sollte diese Erzeugungsform nach Möglichkeit vermieden werden oder auf kürzere HTML - Stücke beschränkt bleiben.

Insbesondere in alten Versionen des Internet-Explorer (bis Version 8) gibt es eine Menge Bugs und Probleme beim Erzeugen von Elementstrukturen aus einer Zeichenkette, von denen viele, aber nicht alle von dElement abgefangen werden. Auch wenn dies größtenteils durchaus möglich wäre, erscheint es aufgrund des Alters dieser Browser und des resultierenden geringen Verbreitungsgrades nicht mehr sinnvoll, dElement wegen dieser Ausnahmen mehr als notwendig aufzublähen.
Einige dieser Bugs lassen sich vermeiden; beispielsweise sollte bei Tabellen konsequent das Element <tbody> verwendet werden; also <table><tbody><tr>…</tr></tbody></table> statt <table><tr>…</tr></table>. Besser ist jedoch, die Struktur über die dElement-Definition zu erzeugen.

Wird das erzeugte Dokument als application/xhtml+xml ausgeliefert, so muß die notierte HTML Zeichenkette valide sein und (fast) alle „named entities“ müssen durch die korrespondierenden numerischen Versionen ersetzt werden; z.B. &nbsp; muß durch &#160; oder &#xA0; ersetzt werden. Erlaubt sind: &gt; &lt; &amp; &quot; &apos;

Bedingtes Setzen einer Teildeklaration

Die Teildeklaration (sowie deren Unter-Deklarationen), in der das „condition“-Schlüsselwort (alternativ: „cond“) vorkommt, wird nur dann eingefügt, wenn die Bedingung erfüllt ist. Es ist programmatisch zu beachten, daß eine nicht erfüllte Bedingung in der obersten Deklarations-Ebene dazu führt, daß dElement()/dAppend() „null“ zurückgibt.

Verwendung von condition / cond
var f = 3, xyz;

// Bedingung erfüllt [3 > 2]
// xyz ist ein p-Element mit Kind-Element (entspricht <p><span>Hallo</span></p>)
xyz = K345.dElement({
	element: 'p',
	child: { element: 'span', text: 'Hallo', cond: f > 2 }
});

// Bedingung nicht erfüllt [3 === 2]
// xyz ist ein leeres p-Element (entspricht <p></p>)
xyz = K345.dElement({
	element: 'p',
	child: { element: 'span', text: 'Hallo', cond: f === 2 }
});

// Bedingung in oberster Ebene; nicht erfüllt
// xyz ist 'null'
xyz = K345.dElement({
	element: 'div',
	cond: 2 === 3,
	child: {element: 'strong', text: 'fett'}
});

Bedingtes Setzen einer Eigenschaft

Über das Schlüsselwort „setif“ kann eine Eigenschaft nur dann gesetzt werden, wenn eine bestimmte Bedingung erfüllt ist. „setif“ erwartet als Wert ein Objekt mit den folgenden drei Eigenschaften:

Ist der Wert der Eigenschaft „name“ die Zeichenkette "child" sowie „value“ ein Objekt / Array / Element, wird dieses an das aktuelle Element angehängt, wenn die Bedingung erfüllt ist.

Die Eigenschaft „setif“ ist multi-fähig

Verwendung von setif
// className wird nur gesetzt, wenn i === 12 ist
{
	element: 'div',
	id: 'foo',
	setif: {
		name: 'className',
		value: 'marked',
		condition: (i === 12)
	}
}

// Spezialfall "child"
var ch = {element: 'strong', text: 'appended'};
// oder
var ch = [{element: 'strong', text: 'append'}, {text: ' me'}];
// oder
var ch = K345.dElement({/* hier Deklaration */});

var div_bar = K345.dElement({
	element: 'div',
	id: 'bar',
	setif: {
		name: 'child',
		value: ch,
		condition: (i % 5 == 0)
	}
});

Initialisierungs-Funktion

NEU: Mit der Eigenschaft „init“ kann eine Referenz auf eine Funktion übergeben werden, welche nach der Erzeugung der Element-Struktur ausgeführt wird. Dadurch kann z.B. ein ausserhalb der erzeugten Struktur liegendes Element abhängig vom Wert eines erzeugten Elements manipuliert werden.

Innerhalb der Funktion steht das aktuelle Element als »this« sowie der gesamte erzeugte Element-Baum als Parameter der Funktion zur Verfügung.

Wichtig: Die init-Funktion wird aufgerufen, sobald dElement den Element-Baum erzeugt hat, d.h. bevor die Elemente ins Dokument integriert werden. Zugriff ist zu diesem Zeitpunkt daher nur per „elrefs“ oder „collect“ möglich.

Verwendung von init
// Beispiel: init
K345.dAppend(document.body, {
	element: 'select',
	name: 'foo',
	child: [{
		element: 'option',
		value: 'hello',
		text: 'first'
	}, {
		element: 'option',
		value: 'world',
		text: 'second',
		selected: true
	}],
	init: function () {
		// ein irgendwo existierendes Element aktualisieren
		// 'this' ist eine Referenz auf das 'select'-Element
		document.getElementById('some_element').textContent = this.value;
	}
});

Elemente mit Schleifen vervielfältigen (Loops)

NEU: dElement bietet eine rudimentäre Unterstützung von Schleifen an, mit denen man gleichartige Elemente / Elementstrukturen leichter erzeugen kann. Dabei können in Zeichenketten-Deklarationswerten Platzhalter gesetzt werden, welche durch den aktuellen Zählwert ersetzt werden.

„loop“ bzw. „loopdeep“ erwarten als Wert entweder eine Ganzzahl ≥0 oder ein Objekt, das mindestens die Eigenschaft „count“ enthält, deren Wert eine Ganzzahl ≥0 ist. Über die optionalen Eigenschaften „start“ und „step“ kann die Schleifenvariable beeinflusst werden. Dazu später mehr.

Ein Platzhalter ist in der einfachsten Form die Zeichenfolge !!n!! bzw. !!c!!. Dieser wird durch den aktuellen Wert der Schleifenvariable ersetzt [Standardmäßig startend bei 0].

Der Unterschied zwischen !!n!! und !!c!! ist, daß der Wert von !!n!! über die Objekt-Eigenschaften „start“ und „step“ angepasst werden kann, während sich der Wert von !!c!! grundsätzlich auf den aktuellen Schleifen-Zähler bezieht. Die weiter unten beschriebenen mathematischen Berechnungen können auf beide Platzhalter angewendet werden.

Der Platzhalter !!v!! dient dazu, Werte aus einem übergebenen Array einzufügen. Dabei wird im ersten Schleifendurchlauf das erste Array-Element, im zweiten Durchlauf das zweite Element usw. verwendet. Das Array wird mittels der Eigenschaft „values“ übergeben.

Ist die Eigenschaft „valuesrepeat“ gesetzt (Wert beliebig), wird beim Erreichen des letzten Array-Elements wieder mit dem Ersten fortgefahren, falls „count“ größer ist als die Länge des Arrays in „values“.

Platzhalter dürfen keine Leerzeichen, Tabulatoren oder sonstige Abstandzeichen oder weitere Buchstaben oder nicht hier dokumentierte Symbole enthalten!

Einfache Verwendung von loop und loopdeep
// Beispiel 1
var el = K345.dElement({
	element: 'span',
	loop: {count: 5}, // oder 'loop: 5'
	text: ' !!n!! ' // einfacher Platzhalter
});

// Beispiele 2 + 3
var el = K345.dElement({
	element: 'p',
	loop: {count: 3}, // Beispiel 3: Schlüsselwort 'loop' durch 'loopdeep' ersetzen
	id: 'p-!!n!!',
	child: { // erste Unterdeklaration; wird ersetzt
		element: 'span',
		id: 'span-!!n!!'
		child: { // zweite Unterdeklaration; wird bei Schlüsselwort 'loop' nicht ersetzt
			element: 'span',
			id: 'subspan-!!n!!',
			text: 'text !!n!!'
		}
	}
});
HTML Notation des erzeugten Elementbaums
<!-- Beispiel 1 -->
<span> 0 </span><span> 1 </span><span> 2 </span><span> 3 </span><span> 4 </span>

<!-- Beispiel 2: Schlüsselwort loop
	Platzhalter nur in aktueller Ebene und erster Unterdeklaration ersetzen -->
<p id="p-0"><span id="span-0"><span id="subspan-!!n!!">text !!n!!</span></span></p>
<p id="p-1"><span id="span-1"><span id="subspan-!!n!!">text !!n!!</span></span></p>
<p id="p-2"><span id="span-2"><span id="subspan-!!n!!">text !!n!!</span></span></p>

<!-- Beispiel 3: Schlüsselwort loopdeep
	Platzhalter in aktueller Ebene und allen Unterdeklarationen ersetzen -->
<p id="p-0"><span id="span-0"><span id="subspan-0">text 0</span></span></p>
<p id="p-1"><span id="span-1"><span id="subspan-1">text 1</span></span></p>
<p id="p-2"><span id="span-2"><span id="subspan-2">text 2</span></span></p>

dElement unterstützt weitere Platzhalter-Optionen, die den Wert beeinflussen können. Möglich sind Addition und Subtraktion[7]. Dazu wird hinter dem »n« (»c«) im Platzhalter eines der Symbole „+“ „-“ sowie die zu addierende bzw. subtrahierende Zahl eingefügt. So wird z.B. durch den Platzhalter !!n+3!! eine Zahlenfolge 3, 4, 5, 6… erzeugt, bei !!n-2!! die Zahlenfolge -2, -1, 0, 1,….

Steht vor dem »n« eine Zahl, wird der Schleifenwert mit dieser Zahl multipliziert. Dadurch können Platzhalter wie !!3n+2!! erzeugt werden (hier wird erst der Zählwert mit 3 multipliziert und dann 2 addiert, was eine Zahlenfolge von 2, 5, 8, 11, 14… ergibt[8]). Es kann optional nach der Zahl ein Multiplikationssymbol (* oder ) eingefügt werden, z.B. !!4•n!!.

Eine weitere Möglichkeit, die Zahlenfolge zu verändern, ist die Verwendung der Eigenschaften „start“ und „step“ im Objektwert von „loop“ bzw. „loopdeep“.

Verwendung von Platzhaltern
// Beispiel 1
var el = K345.dElement({
	element: 'div',
	child: {
		element: 'span',
		loop: {count: 3, start: 1}, // Werte der Schleife sind 1,2,3
		title: 'counter: !!n!!',
		id: 'span-!!3n+2!!', // mit 3 multiplizieren und 2 addieren => 5,8,11
		text: 'text !!2*n!!' // mit 2 multiplizieren => 2,4,6
	}
});

// Beispiel 2
var el = K345.dElement({
	element: 'div',
	child: {
		element: 'span',
		loop: {count: 3, start: 2, step: 4}, // Werte der Schleife sind 2,6,10
		id: 'span-!!n-1!!',   // 1 vom Schleifenwert subtrahieren => 1,5,9
		text: 'text !!n+2!!', // 2 zum Schleifenwert addieren => 4,8,12
		title: 'title !!2*c!!' // c läuft immer als Zähler (0,1,2) => 0,2,4
	}
});

// Beispiel 3
var el = K345.dElement({
	element: 'div',
	child: {
		element: 'span',
		text: 'Die !!n+1!!. Frucht ist !!v!!.',
		loop: {
			count: 3,
			values: ['ein Apfel', 'eine Birne', 'eine Mango']
		}
	}
});

// Beispiel 4
var el = K345.dElement({
	element: 'span',
	text: '!!v!! ',
	loop: {
		count: 16,
		values: ['A', 'B', 'C'],
		valuesrepeat: 1
	}
});
Erzeugtes HTML
<!-- Beispiel 1 -->
<div>
	<span id="span-5" title="counter: 1">text 2</span>
	<span id="span-8" title="counter: 2">text 4</span>
	<span id="span-11" title="counter: 3">text 6</span>
</div>

<!-- Beispiel 2 -->
<div>
	<span id="span-1" title="title 0">text 4</span>
	<span id="span-5" title="title 2">text 8</span>
	<span id="span-9" title="title 4">text 12</span>
</div>

<!-- Beispiel 3 -->
<div>
	<span>Die 1. Frucht ist ein Apfel.</span>
	<span>Die 2. Frucht ist eine Birne.</span>
	<span>Die 3. Frucht ist eine Mango.</span>
</div>

<!-- Beispiel 4 -->
<span>A B C A B C A B C A B C A B C A </span>

[6] Wird in einer Unter-Deklaration eines der Schlüsselwörter „loop“, „loopstop“ oder „loopdeep“ verwendet, wird die aktuelle Ersetzung beendet.

[7] Zumindest vorerst wird auf die Erkennung und Verarbeitung komplexerer Operationen zugunsten von Geschwindigkeit und Speicherverbrauch verzichtet; falls es erforderlich ist, kann — wie es zur Vervielfältigung bisher ohnehin notwendig war — mit einer externen Scheife und einem Array gearbeitet werden.

[8] für !!3n+2!!: 3•0 + 2 = 2; 3•1 + 2 = 5; 3•2 + 2 = 8; 3•3 + 2 = 11; 3•4 + 2 = 14

Events

dElement bietet verschiedene Möglichkeiten, einem Element einen Eventhandler zuzuweisen:

Event-Methode eines Elements
z.B. element.addEventListener()
externe Event-Verwaltung
z.B. Cross-Browser-Methoden
Eigenschaft / Attribut
z.B. onclick, onchange usw.

Event-Methode eines Elements

Zuweisung über das Schlüsselwort „event“. Als Wert wird ein Objekt mit den Eigenschaften „function“ und „arguments“ (alternativ: „func“ bzw. „args“) erwartet.

Der Eigenschaft „func“ muß eine Zeichenkette mit dem Namen der Event-Methode des Elements sein. (z.B. 'addEventListener' oder 'attachEvent'). Wird die Eigenschaft nicht definiert, wird 'addEventListener' als Standard verwendet.

Die Eigenschaft „args“ ist ein Array mit den einzelnen Parametern, die der externen Funktion übergeben werden. Die Reihenfolge im Array muß der Reihenfolge der erwarteten Funktions-Parameter entsprechen.

Event über Element-Methode
// Beispiel: element.addEventListener(typ, func, capture)
var btn = K345.dElement({
	element: 'button',
	text: 'Button 1',
	event: {
		func: 'addEventListener', // muß Zeichenkette sein
		args: [
			'click',
			function () {
				alert('Button wurde betätigt');
			},
			false
		]
	}
});

Event-Verwaltung extern

Zuweisung über das Schlüsselwort „event“. Als Wert wird ein Objekt mit den Eigenschaften „func“ und „args“ erwartet.

Der Eigenschaft „func“ muß eine Referenz auf die externe Event-Funktion zugewiesen werden.

Die Eigenschaft „args“ ist ein Array mit den einzelnen Parametern, die der externen Funktion übergeben werden. Die Reihenfolge im Array muß der Reihenfolge der erwarteten Funktions-Parameter entsprechen.

Da zum Zeitpunkt der Deklaration im Objekt-Literal üblicherweise noch keine Referenz auf das Ziel-Element existiert, dem der Handler zugewiesen werden soll, ist der Platzhalter #el# an der entsprechenden Stelle einzusetzen. Dieser Platzhalter kann entfallen, wenn die externe Funktion die Elementreferenz als ersten Parameter erwartet.

Als weitere möglicher Platzhalter sind #elp# und #elpp# definiert. Hiermit wird das Event statt an das für #el# geltende (aktuell definierte) Element an dessen Eltern- bzw. Großeltern-Element gelegt.

Event über externe Funktion
// Beispiel: externe Funktion "foo.addEvent(element, typ, funktion)"
var btn = K345.dElement({
	element: 'button',
	text: 'Button 2',
	event: {
		func: foo.addEvent, // muß Funktionsreferenz sein
		// der Platzhalter '#el#' könnte hier (optional) entfallen, da die
		// Element-Referenz als erster Parameter erwartet wird.
		args: ['#el#', 'click', function () {
			alert('Button wurde betätigt');
		}]
	}
});

// Beispiel: externe Funktion "bar.myEvent(typ, element, funktion)"
var btn = K345.dElement({
	element: 'button',
	text: 'Button 2',
	event: {
		func: bar.myEvent,
		// hier muß '#el#' verwendet werden, da der erste Parameter von
		// bar.myEvent() nicht die Element-Referenz ist
		args: ['click', '#el#', function () {
			alert('Button wurde betätigt');
		}]
	}
});

on… Eigenschaft

„Klassische“ Event-Zuweisung; entspricht element.on<event_type> = <function reference>

Event über Attribut / Eigenschaft
// Beispiel: direkte Zuweisung über Eigenschaft onclick
var btn = K345.dElement({
	element: 'button',
	text: 'Button 3',
	onclick: function () {
		alert('Button wurde betätigt');
	}
});

„event“ ist eine multi-Eigenschaft

Element-Referenzen


elrefs

Enthält die Baum-Deklaration das Schlüsselwort „elrefs“ und dessen Wert ist ein Objekt, so werden sämtliche erzeugten Elemente mit „id“ oder „name“-Eigenschaft in diesem Objekt abgelegt. Es werden dazu zwei Unterobjekte „i“ (für IDs) und „n“ (für Namen) angelegt, die jeweils die Elemente aufnehmen. Da mehrere Elemente denselben Namen nutzen können, ist der Datentyp von obj.n[name] immer ein Array.

Element-Referenzen: Schlüsselwort „elrefs“
// Objekt, das die Element-Referenzen enthalten soll.
// Die Unterobjekte „i“ und „n“ werden automatisch angelegt, sofern sie noch
// nicht vorhanden sind.
var myrefs = {};

var irgendwas = K345.dElement({
	element: "div",
	id: "einDiv",
	elrefs: myrefs,  // Hier wird Objekt zur Referenzspeicherung übergeben
	child: {
		element: "input",
		type: "text",
		id: "einInput",
		name: "roger_stan_klaus"
	}
});

// myrefs.i.einDiv enthält nun die Referenz auf das div-Element
// Dies entspricht der Referenz mit document.getElementById("einDiv")
alert(myrefs.i.einDiv); // [object HTMLDivElement]

// Da das input-Element sowohl eine „name“ wie auch eine „id“-Eigenschaft besitzt,
// ist diese Referenz sowohl unter myrefs.i.einInput wie auch unter
// myrefs.n.roger_stan_klaus[0] abgelegt.
alert(myrefs.i.einInput); // [object HTMLInputElement]

// Mehrere Elemente können den gleichen Namen besitzen, daher ist es erforderlich,
// einen Index zu verwenden
alert(myrefs.n.roger_stan_klaus[0]); // [object HTMLInputElement]

Das Referenzobjekt wird für alle Aufrufe von dElement verwendet, bis ein anderes Objekt oder „null“ zugewiesen wird.

Die Deklaration wird von Oben nach Unten verarbeitet, d.h. wenn beispielsweise elrefs: null in einem Unterzweig gesetzt wird, werden auch Elemente in höheren Ebenen, die später deklariert werden, nicht mehr erfasst. Durch erneutes Setzen von elrefs mit der Objektreferenz ab dem wieder zu erfassenden Element werden die Referenzen wieder gesammelt.

Element-Referenzen: ein/ausschalten
var myrefs = {};
K345.dElement([{
	element: 'div',	id: 'd01', text: 'div1',
	elrefs: myrefs // referenzen speichern
}, {
	element: 'div', id: 'd02', text: 'div2',
	child: { element: 'span', text: 'span1', id: 's01' }
}, {
	element: 'div', id: 'd03', text: 'div3',
	elrefs: null, // ab hier nicht speichern
	child: { element: 'span', text: 'span2', id: 's02' }
}, {
	element: 'div', id: 'd04', text: 'div4',
}, {
	element: 'div', id: 'd05', text: 'div5',
	elrefs: myrefs, // ab hier wieder speichern
	child: { element: 'span', text: 'span3', id: 's03' }
}, {
	element: 'div', id: 'd06', text: 'div6',
}]);
// myrefs.i enthält nun Referenzen auf d01, d02, s01, d05, s03 und d06
// nicht vergessen: Deklaration wird von Oben nach Unten verarbeitet

collect

Um in einer Baum-Deklaration die Referenzen auf bestimmte Elemente zu erhalten, kann für jedes Element mit dem Schlüsselwort „collect“ die Referenz auf ein Array übergeben werden. Jedes erzeugte Element mit diesem Schlüsselwort wird dann in der Reihenfolge der Erzeugung in diesem Array abgelegt.

Alternativ ist es möglich, ein Objekt mit den Eigenschaften „obj“ mit einer Objekt-Referenz und „name“ mit einem eindeutigen Namen zu übergeben. Die Referenz auf das erzeugte Element wird dann in obj[name] gespeichert.

Verschiedenen Elementen können jeweils separate Arrays bzw. Objekte zugewiesen werden.

„collect“ Beispiel 1: Array
/* ------ Beispiel 1 ------- */
var my_el = [],   // Nimmt Element-Referenzen auf
	more_el =[],  // Nimmt Element-Referenzen auf
	els;

els = K345.dElement({
	element: "ul",
	collect: more_el,
	child: [{
		element: "li",
		child: {
			element: "button",
			name: "bar",
			collect: my_el,
			text: "Klick mich"
		}
	}, {
		element: "li",
		collect: more_el,
		child: {
			element: "button",
			name: "bar",
			collect: my_el,
			text: "Klick mich nicht"
		}
	}]
});
// more_el enthält Referenzen auf „ul“ und das zweite „li“
// my_el enthält Referenzen auf zwei „button“-Elemente
„collect“ Beispiel 2: Objekt
/* ------ Beispiel 2 ------- */
var hg = {},
	els;

els = K345.dElement({
	element: 'div',
	collect: {obj: hg, name: 'mydiv-1'},
	child: {
		element: 'span',
		collect: {obj: hg, name: 'myspan-1'}
	}
});

// Das übergebene Objekt 'hg' wurde nun folgendermaßen befüllt (in Objekt-Literal-
// Schreibweise verdeutlicht):
{
	'mydiv-1': Referenz_auf_div_element,
	'myspan-1': Referenz_auf_span_element
}
„collect“ Beispiel 3: Schleife Teil-Deklarationen
/* ------ Beispiel 3 ------- */
var inp = [], // Nimmt Element-Referenzen auf
	dv = [],
	els = [],
	i;

// erzeuge 10 div elemente mit entsprechenden Kind-Elementen
// <div id="mydiv0"><p>Lorem ipsum...</p><input name="myinput" id="myinp_0"></div>
// ...
// <div id="mydiv9"><p>Lorem ipsum...</p><input name="myinput" id="myinp_9"></div>
for (i = 0; i < 10; i++) {
	els[i] = {
		element: "div",
		id: "mydiv" + i,
		collect: dv // Alle div-Elementreferenzen im Array "dv" sammeln
		child: [{
			element: "p",
			text: "Lorem ipsum dolor..."
		}, {
			element: "input",
			name: "myinput",
			id: "myinp_" + i,
			collect: inp    // Jede Referenz des erzeugten input-Elements wird
							// in Array „inp“ abgelegt
		}]
	};
	document.body.appendChild(K345.dElement(els));
}
alert(inp.length); // 10
alert(inp[5].id);  // myinp_6
„collect“ Beispiel 4: Mehrere Referenz-Sammlungen
/* ------ Beispiel 4 ------- */
var hg = {}, moo = [], els;

// Beachtenswert: Die beendenden Namenteile des collect-Schlüsselworts haben keinen
// Einfluss auf die Element-Sammlungen
els = K345.dElement({
	element: 'div',
	id: 'mydiv-1',
	collect_1: {obj: hg, name: 'd1'},
	collect_2: moo,
	child: [{
		element: 'span',
		id: 'myspan-1',
		collect_me: {obj: hg, name: 's1'}
	}, {
		element: 'span',
		id: 'myspan-2',
		collect: {obj: hg, name: 's2'},
		collect_2: moo
	}, {
		element: 'span',
		id: 'myspan-3',
		collect_moo: moo
	}]
});

// das Array 'moo' enthält nun die Element-Referenzen mit folgenden IDs:
//    moo = [ div#mydiv-1, span#myspan-2, span#myspan-3 ]
//
// das Objekt 'hg' enthält die Element-Referenzen mit folgenden IDs:
//    hg = { d1: div#mydiv-1, s1: span#myspan-1, s2: span#myspan-2 }

Die Helferfunktion dAppend


Node|Fragment reference = K345.dAppend(
String|Node target id|reference,
Object declaration
[, Const mode_flag = K345.DAPPEND_APPEND ]
);

Um den Elementbaum zu erstellen und direkt an ein bestehendes Element anzuhängen, gibt es die kleine Helferfunktion dAppend, die alle Schritte ausführt.

Als ersten Parameter erwartet sie eine Referenz auf das Element (oder eine Zeichenkette, die einer Element-ID entspricht), an welches der erzeugte Elementbaum angehängt werden soll; als zweiten Parameter eine beschreibende Baum-Deklaration wie bei dElement. Der optionale dritte Parameter legt den Modus fest, mit dem der erzeugte Elementbaum relativ zum übergebenen Element ins DOM eingehängt wird.

Zur weiteren Verarbeitung gibt die Funktion zusätzlich den erzeugten Element-Baum zurück (oder „null“, falls ein Fehler aufgetreten sein sollte)

var tree = K345.dAppend("my_element", el_def);

Der Code aus dem Beispiel könnte mit dAppend folgendermaßen vereinfacht werden:

Schreibweise unter Verwendung der Funktion dAppend
// Alternativ unter Verwendung von dAppend()
var my_link = K345.dAppend( "my_element", {
	element: "a", href: link_href, title: "link to new page", child: {
	element: "span", child: [
		{element: "img", src: img_src, width: img_w, alt: "an arrow"},
		{text: "some text"}
	]}
});

Auswirkung der Modus-Flags

dAppend bietet über Modus-Flags die Möglichkeit, erzeugte Elemente auf verschiedene Weise relativ zum Referenz-Element in den vorhandenen Element-Baum einzuhängen. Dies kann entweder durch eine der vordefinierten Konstanten geschehen oder durch eine Zeichenkette ähnlich wie bei z.B. insertAdjacentElement.

Warnung Die Modus-Flags dürfen nicht kombiniert werden!

Beispiel: Vorhandenes Element<p id="my_p">hello</p>
Aufruf dAppend
// Aufruf für alle Beispiele:
K345.dAppend(
	'my_p',
	{element: 'span', text: ' world '} // erzeugt <span> world </span>
	<APPEND_MODUS>
);
Bei K345.DAPPEND_APPEND, K345.DAPPEND_LAST oder 'beforeEnd' wird das erzeugte Element als weiteres Kind des vorhandenen Elements am Ende eingehängt. Dies ist die Standardvorgabe und muß nicht explizit angegeben werden.
<p id="my_p">hello<span> world </span></p>
Bei K345.DAPPEND_FIRST oder 'afterBegin' wird das erzeugte Element als erstes Kind des vorhandenen Elements am Anfang eingehängt:
<p id="my_p"><span> world </span>hello</p>
Bei K345.DAPPEND_AFTER oder 'afterEnd' wird das erzeugte Element hinter dem vorhandenen Element eingehängt:
<p id="my_p">hello</p><span> world </span>
Bei K345.DAPPEND_BEFORE oder 'beforeBegin' wird das erzeugte Element vor dem vorhandenen Element eingehängt:
<span> world </span><p id="my_p">hello</p>
Bei K345.DAPPEND_WIPE oder 'wipeContent' werden alle vorhandene Kind-Knoten gelöscht und das erzeugte Element als einziges Kind-Element angehängt:
<p id="my_p"><span> world </span></p>
Bei K345.DAPPEND_REPLACE oder 'replaceElement' wird das vorhandene Element durch das erzeugte Element ersetzt:
<span> world </span>

Der Code

Downloads

Kompatibilität

Benötigt einen ES5-kompatiblen Browser. (so ziemlich alle ab 2012/2013)

Sollte dElement in einem Browser nicht ordnungsgemäß funktionieren, bitte ich um genau Beschreibung des Fehlers.

Die spezielle Unterstützung für Internet-Explorer 6-8 wurde größtenteils entfernt. dElement sollte theoretisch mit IE8 weiterhin funktionieren, allerdings ungetestet.

Umwandlungstabelle für Attribut-Namen

In Javascript müssen diverse Eigenschaftsnamen eine ganz bestimmte Schreibweise haben. So kann beispielsweise elem.for = "foo"; zum Setzen der Eigenschaft „for“ bei Label-Elementen nicht verwendet werden, da for innerhalb von Javascript bereits eine Bedeutung als Schüsselwort hat. Hier wäre elem.htmlFor = "foo"; zu verwenden.

dElement wandelt die Eigenschaftsnamen anhand dieser Tabelle entsprechend um, sodaß man beim Erstellen einer Baum-Deklaration die üblichen Attributnamen wie auch in HTML verwenden kann. (Bei Javascript-eigenen Schlüsselwörtern wie z.B. for müssen die Attributnamen als Zeichenkette notiert werden -> "for":

Attributnamen-Umwandlung
/** conversion table for HTML-attribute names
	@type {object} */
K345.attrNames = K345.attrNames || {
	acceptcharset: 'acceptCharset', accesskey: 'accessKey', alink: 'aLink',
	bgcolor: 'bgColor', cellindex: 'cellIndex', cellpadding: 'cellPadding',
	cellspacing: 'cellSpacing', charoff: 'chOff', 'class': 'className',
	codebase: 'codeBase', codetype: 'codeType', colspan: 'colSpan',
	datetime: 'dateTime', 'for': 'htmlFor', frameborder: 'frameBorder',
	framespacing: 'frameSpacing', ismap: 'isMap', longdesc: 'longDesc',
	marginheight: 'marginHeight', marginwidth: 'marginWidth', maxlength: 'maxLength',
	nohref: 'noHref', noresize: 'noResize', nowrap: 'noWrap',
	readonly: 'readOnly', rowindex: 'rowIndex', rowspan: 'rowSpan',
	tabindex: 'tabIndex', usemap: 'useMap', valign: 'vAlign', vlink: 'vLink'};

Diese Umwandlungstabelle ist in der Download-Datei delement.js enthalten.