DOM

Funktionen und Objekte für die Manipulation des DOM.

Dataset

Das Dataset-Objekt ist eine Erweiterung des Map-Objektes und für die Verwendung mit dem Template-Objekt optimiert.

dataset = new Alvine.DOM.Dataset();
dataset.set('h1', 'My Headline');

Datasets lassen sich auch beliebig verschachteln und mit anderen Objekten füllen.

dataset = new Alvine.DOM.Dataset();
inner = new Alvine.DOM.Dataset();
dataset.set('index0', inner);

Datasets lassen sich auch durch einfache Objekte initialisieren. Dazu muss als Wert im Konstruktor lediglich ein Objekt übergeben werden. Der Konstruktor erstellt aus diesem Objekt eine Datenstruktur aus Alvine.Types.Map für Objekte und Alvine.Types.Collection für echte Arrays

obj = {
    "headline": "Staedte",
    "cities": [
        "Muenchen",
        "Frankfurt",
        "Berlin"
    ]
};

dataset = new Alvine.DOM.Dataset(obj);

dataset.get('cities')
// ist vom Typ Alvine.Types.Collection

dataset.get('cities').toString
// -> Muenchen,Frankfurt, Berlin

Datasets können auch automatisch über einen Tageingebunden werden. Dazu muss ein HTML-Tag mit der Klasse auto-dataset in die HTML Seite eingefügt werden. Das Dataset kann entweder direkt per Attribute definiert werden data-dataset oder über Ajax nachgeladen werden data-dataurl. Das eingelesene Dataset wird in die Registry unter dem Namen der mit dem Attribute data-datakey angegeben wurde eingetragen.

<div class="auto-dataset" data-dataset="{'title':mydataset}" data-datakey="mykey"></div>

Das erstellte Dataset kann nun mit Javascript ausgelsen werden.

dataset = Alvine.Registry.get('mykey');

Wurde kein Key definiert, so wird ein Zufallswert für den Schlüssel genommen.

Wurde eine URL definiert und wird zusätzlich das Attribute data-bind angegeben, so werden Änderungen am Dataset per POST an diese URL übertragen.

<div class="auto-dataset" data-dataurl="http://example.com/mydata.json" data-bind="true"> </div>

Mit der Methode dataset.initMutationObserver() wird ein MutationObserver initalisiert, der das DOM beobachtet und bei Änderungen am DOM die Observer im Dataset rekursive bereinigt.

Objektreferenz

Dataset(initValues)

Beschreibung

Neues Objekt erstellen


Parameter-Liste

initValues (Alvine.Types.Map | Object | Array)

Werte mit denen das Dataset initialisiert wird. Aus Objekten werden Maps und aus Arrays Collection erstellt.


Rückgabewert

Gibt ein neues Object vom Typ Dataset zurück.

Dataset.initMutationObserver()

Beschreibung

Beobachten von Änderungen am DOM und entfernen von Observern, die durch ein bind erstellt wurden.


Rückgabewert

Gibt die eigene Instanz zurück.

Dataset.toJSON()

Beschreibung

Diese Methode sollte nicht direkt aufgerufen werden. Diese Methode wird von JSON.stringify() aufgerufen. Siehe hierzu auch die Beschreibung bei developer.mozilla.org


Rückgabewert

Daten des Dataset in der von JSON.stringify geforderten Form.

Dataset.toString()

Beschreibung

Erstellt aus den Daten eine Zeichenkette


Rückgabewert

Daten des Dataset als Zeichenkette (String)

Template

Das Templateobjekt erlaubt es mit dem neuen HTML5 Template-Tag zu arbeiten. Wie in dem folgenden Beispiel zu sehen ist der neue Template-Tag eine gute Möglichkeit HTML-Templates direkt im HTML zu definieren. Der Vorteil der Templates ist, das diese zur Ladezeit nicht gerendert werden und somit keinen nenswerten Speicher und Rechenleitung verbrauchen.

<template id="mytemplate" class="templates">
<div>
    <h1>Template 2</h1>
    <p>Platzhalter</p>
</div>
</template>

Auf dieses Template kann nun einfach über das Template-Objekt zugegriffen werden.

template = new Alvine.DOM.Template('#mytemplate');

Werden keine Templates mit dem Namen gefunden so wird eine Exception geworfen. Werden mehrere Templates gefunden, so wird mit get(0) das erste aus der Liste der gefundenen genommen. Um das Template in eine andere Struktur einfügen zu können, kann man mit der Methode Template.getjQueryObject() ein jQuery-Objekt erstellen und dieses per jQuery.append() einfügen.

Neben einem Selector kann ein Template auch direkt aus einem jQuery-Objekt erstellt werden

template = new Alvine.DOM.Template(jQuery('#mytemplate'));

Alternativ kann man sich per Template.getHtmlFragment() ein echtes DOM-Objekt holen.

obj = template.getjQueryObject();
// -> jQuery-Objekt
jQuery('<div/>').append(obj);

template.getHtmlFragment()
// -> Document-Fragment Objekt

Den Methoden Template.getjQueryObject() und Template.getHtmlFragment() kann ein Dataset übergeben werden. Das Dataset dient dazu Werte innerhalb des Templates zu ersetzen.

_Der Schlüssel ___INDEX ist ein geschützter Wert, der intern für den Index benutzt wird. Datasets dürfen keinen Wert mit diesen Schlüssel enthalten.

Hinweis

Um Änderungen an Datasets im DOM abzubilden wird im Dataset ein Observer eingehängt. Wird das per bind gebundene Element aus dem DOM gelöscht, so bekommt das dataset das nicht mit und der Observer im dataset bleibt zurück. Damit dies nicht passiert, muss das dataset über einen MutationObserver verfügen: dataset.initMutationObserver()

Zum Thema MutationObserver siehe auch folgende Quellen

Sequenzdiagramm

Sequenzdiagramm

Objektreferenz

Template(selector)

Beschreibung

Die Methode sucht über den Selektor im aktuellen Dokument das entsprechende Template. Werden mehrere Templates gefunden, so wird nur das erste Template genommen.


Parameter-Liste

selector (String | jQuery)

jQuery-Selektor wie in der jQuery-Dokumentation beschrieben.


Rückgabewert

Neues Objekt vom Typ Template

Template.getjQueryObject(map)

Beschreibung

Diese Methode ruft Template.getHtmlFragment(map, false) auf.


Parameter-Liste

map (Alvine.Types.Map)

Daten, die für das Erstellen des HTML-Fragments verwendet werden sollen.


Rückgabewert

Gibt ein HTMLFragment zurück

Template.getHtmlFragment(dataset, elementFlag)

Beschreibung

Liefert das Template zurück.


Parameter-Liste

dataset (Alvine.Types.Map | Alvine.DOM.Dataset)

Die zum Erstellen des HTML-Fragments zu verwendende Daten. Wird kein Dataset übergeben, so wird das im Template hinterlegte Dataset genutzt.

elementFlag (Boolean)

Bei true wird eine DOM-Node und bei false ein jQuery-Objekt zurückgegebe


Rückgabewert

Gibt ein HTMLFragment als DOMNode oder jQuery zurück.

Template.toString()

Beschreibung

Die Methode erstellt ein HTML-Fragment und wandelt diese in eine Zeichenkette um.


Rückgabewert

Gibt eine Zeichenkette mit dem entsprechenden HTML zurück.

Wichtig

Hierbei gehen alle Bindungen der Nodes verloren

Inhaltsmanipulation

In Anlehnung an die PHP-Erweiterung Alvie.Markup.HTML können auch im Javascript data- Attribute zum Einsatz kommen.

<template id="template">
<div>
    <h1 data-replace="dataset:h1">Template</h1>
</div>
</template>

Um das Template mit Werten zu füllen, muss ein Dataset erstellt und mit Werten erfüllt werden

dataset new Alvine.DOM.Dataset();
dataset.set('h1', 'My Headline');
mytemplate = template.getjQueryObject(dataset);

Das Ergebnis-HTML in mytemplate hat dann folgenden Inhalt

<div>
    <h1>My Headline</h1>
</div>

Neben der einfachen Ersetzung können auch noch weitere Befehle durch Pipes getrennt im Attribute übergeben werden. Die einzelnen Befehle bearbeiten immer die Ausgabe des vorherigen Befehls.

<template id="template">
<div>
    <h1 data-replace="dataset:h1 | strtolower">Template</h1>
</div>
</template>

Das Ergebnis des folgenden Templates ist dann eine komplett kleingeschriebene Überschrift.

<div>
    <h1>my headline</h1>
</div>

Das Template-Objekt verwendet für die Transformation der Werte die Methode transformValues.

Über die Operation Eigenschaft des Template-Objektes lassen sich weitere Funktionen einfügen. Somit lassen sich alle Manipulationen des Wertes bewerkstelligen. Die eigene Operation wird über das call Schlüsselwort aufgerufen. Alle Werte die nach dem Namen der Funktion per Doppelpunkt angegeben werden, werden direkt an die Funktion übergeben.

<p data-replace="dataset:key | call:getFullname:!">Text</p>

// Template-Objekt erstellen
template = new Alvine.DOM.Template(selector);

// Operation definieren
template.Operation.getFullname = function(value, sign) {
    return value+sign;
};

// Der Operator muss vor dem Aufruf der Funktion getHtmlFragment() definiert sein.     
template.getHtmlFragment(dataset);

Ein weiterer Befehl ist data-replaceself. Hier wird nicht der Wert des Elements gesetzt, sondern das gesamte Element ersetzt.

<template id="template">
<div>
    <h1 data-replaceself="dataset:h1 | strtolower">Template</h1>
</div>
</template>

Führt also zu folgendem Ergebnis ohne H1 Tags

<div>
    my headline
</div>

Attribute lassen sich ebenso einfach über ein Dataset setzen. Dazu muss im Tag nur der Befehl data-attributes gesetzt werden.

<template id="template7" class="templates">
<div>
    <a data-attributes="href dataset:url | strtolower, style static:color:red, class dataset:class | strtoupper">Link</a>
</div>
</template>

Möchte man als Wert des Attributes das Attribut eines anderen Objektes auslesen und verwenden, so bietet sich jQuery an. Kombiniert mit einem Präfix oder Suffix können so schnell und einfach ID gesetzt werden. Der erste Parameter nach jQuery ist der Selektor, gefolgt von der jquery-Funktion attr und dem Namen des gewünschten Attributes. Um den Wert eines anderen Elements auszulesen, muss `val und für Eigenschaften prop verwendet werden (entsprechend der jQuery Dokumentation).

<input data-attributes="id jquery:#todolisttemplate:attr:id | suffix:-{INDEX}">

Template-Tags eignen sich optimal für die Bereitstellung der benötigten HTML-Struktur. Allerding benötigt man manchmal ein Template aus einer bestehenden Struktur. Auch damit kann das Template-Objekt umgehen.

<div id="domtemplate" data-attributes="id static:newid">
    <p><i data-replace="static:testvalue"></i></p>
</div>

Beim Erstellen des Template-Objektes den gewünschten Selektor angeben. Der umschließende Tag wird in diesem Fall allerdings im Template verwendet und nicht wie bei den Template-Tags entfernt. Deshalb ist es wichtig, falls das Element wie im obigen Beispiel eine ID besitzt, eine neue ID mittels Attribute-Ersetzung zu setzen.

template = new Alvine.DOM.Template('#domtemplate');

Eine weitere Funktionalität des Template-Objektes ist das Wiederholen von Werten aus einer Collection. Dadurch lassen sich Datasets einfach in HTML-Abbilden. Im folgenden Beispiel soll eine Tabelle mit Städten ausgegeben werden. Dazu wird ein HTML-Template mit einer Tabelle und mit der zu wiederholenden Zeile angelegt.

<template id="template" class="templates">
    <div>
        <table>
            <tr data-repeat="city dataset:list">
                <td data-replace="dataset:city">placeholder</td>  
            </tr>
        </table>
    </div>
</template>

Im nächsten Schritt muss das Dataset definiert werden.

template = new Alvine.DOM.Template('#template9');
// Collection mit Städten
list = new Alvine.Types.Collection();
list.append('München');
list.append('Frankfurt');
list.append('Berlin');

// Collection dem Dataset hinzufügen
dataset = new Alvine.DOM.Dataset();
dataset.set('list', list);

// Diese Anweisung erstellt das fertige HTML 
template.getHtmlFragment(dataset);

Das Ergebnis dieses Javascripts ist folgender HTML Schnipsel.

<table>
    <tbody>
    <tr>
        <td>München</td>  
    </tr>
    <tr>
        <td>Frankfurt</td>  
    </tr>
    <tr>
        <td>Berlin</td>  
    </tr>
    </tbody>
</table>

Neben dem Zugrif auf das Dataset, kann man auch auf Datesets in der Registry zugreifen. Hierzu muss nur das Schlüsselwort registry in dem Auswahlattribute geschrieben werden.

<tr data-repeat="city registry:key">

Das Dataset kann neben der Zuweisung durch die Programmierung auch über HTML-Attribute zugewiesen werden. Dazu kann das Dataset entweder direkt im Tag angegeben werden oder es wird eine URL definiert über die das Dataset nachgeladen wird.

<!-- Beispiel mit Dataset im Attribute -->
<template id="template" class="templates" data-dataset='{"headline": "Staedte","list": ["Muenchen","Frankfurt","Berlin"]}'>
    <div>
        <table>
            <tr data-repeat="city dataset:list">
                <td data-replace="dataset:city">placeholder</td>  
            </tr>
        </table>
    </div>
</template>

Wichtig beim Nachladen des Datasets ist es, dass als Ergebnis ein JSON-Objekt zurückgegeben wird.

<!-- Beispiel mit URL -->
<template id="template" class="templates" data-dataurl='http://www.example.com/dataset.json'>
    <div>
        <table>
            <tr data-repeat="city dataset:list">
                <td data-replace="dataset:city">placeholder</td>  
            </tr>
        </table>
    </div>
</template>

Beim Nachladen der Daten von einer URI gibt es eine Besonderheit zu beachten. Da Ajax-Requests asynchrone ablaufen steht das Dataset nicht sofort zur Verfügung. Vielmehr muss vor der Aufführung der Template.getHtmlFragmen() Funktion im Code auf das loaddataset.DONE Event geprüft werden.

jQuery(window).on('loaddataset.DONE', function(m, p) {
    try {
        template.getHtmlFragment();
    } catch(e) {
        // ..
    }
});

Der Fehlerfall wird über das Event loaddataset.FAIL gemeldet.

Ganz ohne Javascriptprogrammierung kann man ein Template über die auto-template Klasse initialisieren. Dazu muss man neben der auto-template Klasse, dem Dataset (als URL oder Json), auch noch ein Target definieren.

<template class="auto-template" data-target="#mytarget" data-dataset=...

Das Target-Attribute muss ein jQuery-Selector sein und dient als Ziel für das erstellte HTML-Fragment. Das HTML-Fragment wird per jQuery(target).append(template) in das Dokument eingefügt.

<div id="mytarget"></div>

Die Funktionsweise ist dann identisch zu den oben beschrieben Funktionen.

Element aus Template erstellen

in der anderen Richtung funktioniert die Zuweisung über die Klasse auto-render. Ein Element das diese Klasse besitzt braucht zusätzlich die Attribute data-template und data-dataset oder data-dataurl.

Die Funktionsweise und Konfiguration ist identisch. Als Template wird das erste, über den im data-template Attribute definierten Selektor, gefundene Element genommen.

<div>

DOM-Elemente an Dataset binden

Möchte man einzelne Werte oder eine ganze Collection auf Änderungen überwachen, so kann man das per data-bind-Attribute im Template definieren. Im folgende Beispiel wird ein Template mit einer Überschrift und einer Tabelle mit einer Spalte definiert.

<div id="targetForTemplate"></div>

<template id="listTemplate">
    <div>
        <h2 data-replace="dataset:headline" data-bind="true" data-bind-tags="my headline">dummy</h2>>dummy</h2>
        <table>
            <tr data-repeat="city dataset:list" data-bind="true">
                <td data-replace="dataset:city">placeholder</td>  
            </tr>
        </table>
    </div>
</template>

Um das Template mit Daten zu füllen, wird ein Dataset aus einer Liste mit den drei Städten München, Frankfurt und Berlin, erstellt.

dataset = new Alvine.DOM.Dataset({"headline": "Staedte", "list": ["München", "Frankfurt", "Berlin"]});

template = new Alvine.DOM.Template('#listTemplate');
// Zuweisen des Templates in einen Div-Container
jQuery('#targetForTemplate').append(template.getHtmlFragment(dataset));

Nach dem Laden der Seite wird eine Tabelle angezeigt. Ändert man nun Werte im Dataset, so ändert sich auch die Darstellung in der Webseite.

// Überschrift neu setzen
dataset.set('headline', 'Meine Städte');
// Einen Wert zur Liste hinzufügen
dataset.get('list').append('Bern');
// Berlin entfernen
dataset.get('list').remove('Berlin');

Der Observer des Bindings kann mit Tags ausgezeichnet werden. Hierzu kann man das Attribute data-bind-tags verwenden.

<h2 data-replace="dataset:headline" data-bind="true" data-bind-tags="my headline">dummy</h2>

Der Observer enthält jetzt die beiden Tags my und headline die zum Beispiel für Abfragen oder das debuggen hilfreich sind.

AutoInit

Die Methode `AutoInit.run() wird von der Renderfunktion des Templates aufgerufen und hilft dabei Eventhandler und Initialisierungen nach einer neuen Darstellung von Inhalten zu initialisieren. Das Objekt Alvine.DOM.AutoInit hat eine Collection als Prototyp und besitzt somit alle Methoden einer Collection.

Eine neue Initialisierungsfunktion wir folgendermaßen integriert:

Alvine.DOM.AutoInit.append(function() {
    // .....
})

Werden über das AutoInit DOM-Elemente initialisiert, so muss jede Initialisierungsfunktion selber dafür sorgen das es zu keiner doppelten Initialisierung kommt.