tinyJS - ein kleines JS-Toolkit

tinyJS ist ein auf das Wesentliche reduzierte JS-Toolkit für regelmäßig wiederkehrende Aufgaben. Es ist in der debugbar minifizierten gzip'ten Version immer noch kleiner als 5kb, so dass es auch in Umgebungen eingesetzt werden kann, in denen Größe eine maßgebliche Rolle spielt.

Download

tiny.js [20788b] - tiny-min.js.gz [3816b]

Cheat Sheet

Für den beschäftigten Entwickler ist ein Cheat Sheet immer ausgesprochen nützlich.

Kompatibilität

Die folgenden Browser sollen standardmäßig unterstützt werden:

Es ist beispielsweise denkbar, den Support zusätzlicher älterer Browser als Plugin zu erzeugen. Neuere Browser sollten von Haus aus unterstützt werden. In späteren Versionen ist insbesondere Support für den mobile Safari und mobile Chrome geplant, da ein Einsatz von tiny.js besonders auf diesen Geräten interessant wird.

Der Quirks-Mode im IE wird von tiny.js absichtlich nicht unterstützt. Einerseits spart der Verzicht auf die Unterstützung einigen Platz, andererseits ist die Verwendung des Quirks Modes nicht mehr zeitgemäß - ein Versagen des Toolkits ist eine vergleichsweise milde Strafe für derartiges Fehlverhalten.

Alphabetische Auflistung der Methoden

DOM

t - DOM Selection

tinyJS unterstützt von Haus aus CSS-Selektoren nach CSS2.1-Standard.

nach oben

  1. es war einmal
  2. und ist nicht mehr
  3. ein ausgestopfter
  4. Teddybär

Normaler Text. Ein Link nach oben

DOM Nodes selektieren:

t([selector:String], [base:Node, optional], [methodless:Boolean(true), optional])

returns Object with Node selection and very interesting methods (unless "methodless" is true, in this case, just an Array of nodes is returned).

t('#testfeld li', document)

t.cf - CSS-Filter

Hier versteckt sich die Sektion mit den CSS-Filtern. Diese kann man auch leicht manuell um zusätzliche Pseudoattribute erweitern. Die vorliegenden CSS-Filter sind weitestgehend CSS2.1-Kompatibel, sowohl mit geringfügigen Einschränkungen als auch Erweiterungen. Jeder Filter hat einen Namen, als Parameter mindestens ein Node-Array (wobei Pseudoattribute einen zusätzlichen Wert in Klammern als Parameter übergeben bekommen können).

t.cf[name]([node, node2, ...], value[, value2])

Basic Types

body (Tagname)

Filtert Nodes nach ihrem Tag-Namen (bspw. 'body'):

document.body === t.cf['']([document.body], 'body')[0] t.cf['']([document.body], 'li').length===0
.class

Filtert Nodes nach einem ihrer Klassennamen. Wenn es um Child-Elemente geht, wird als Abkürzung getElementsByClassName verwendet, sofern vorhanden:

t.cf['.']([document.getElementById('testfeld')], 'test').length === 1 t.cf['.']([document.body], 'test').length === 0
#id

Filtert Nodes nach ihrer genauen ID:

t.cf['#']([document.getElementById('testfeld')], 'testfeld').length === 1 t.cf['#']([document.getElementById('testfeld')], 'test').length === 0

Traversing

* (Everything)

Sucht rekursiv alle Kind-Elemente mit getElementByTagName('*'):

t.cf['*']([document.getElementById('testfeld')]).length === 25 t.cf['*']([document.getElementById('tcfall')]).length === 0
> (Direct Childs)

Liefert alle direkten Kind-Nodes zurück:

t.cf['>']([document.getElementById('testfeld')]).length === 3 t.cf['>']([document.getElementById('tcfall')]).length === 0
~ (Siblings)

Bringt alle Elemente auf gleicher Ebene hinter dem Element:

t.cf['~'](document.getElementById('testfeld').getElementsByTagName('h1')).length === 2 t.cf['~'](document.getElementById('top').getElementsByTagName('span')).length === 0
+ (Next)

Gibt das nächste Element auf gleicher Ebene wider, sofern vorhanden:

t.cf['+'](document.getElementById('testfeld').getElementsByTagName('h1')).length === 1 t.cf['+']([document.getElementById('copyright')]).length === 0
< (Parent)

Dieser Filter entspricht nicht den CSS-Standards, ist aber dennoch ungemein praktisch. Er gibt das jeweilige Eltern-Element wider:

t.cf['<'](document.getElementById('testfeld').getElementsByTagName('h1'))[0] === document.getElementById('testfeld') t.cf['<']([document]).length === 0
<< (Parents)

Auch dieser Filter ist nicht standardkonform, liefert aber alle Parents bis hin zum document:

t.cf['<<']([document.getElementById('testfeld')]).length === 3 t.cf['<<']([document]).length === 0

Attributes

[key], [key=value] (Present/Equals)

Dieser Selektor-Filter filtert das Vorhandensein oder den Wert von Attributen. Für * [name=...] gibt es eine Abkürzung über getElementsByName.

t.cf['='](document.getElementsByTagName('*'), 'name').length === 8 t.cf['='](document.getElementsByTagName('*'), 'title', 'wurl') === 0
[key!=value] (Not Equals)

Gibt eine Collection zurück, deren Nodes im Attribut einen anderen Wert als den Angegebenen haben:

t.cf['!='](document.getElementById('testfeld').getElementsByTagName('*'), 'name', 'hidden1').length === 7 t.cf['!=']([document.getElementById('testfeld')], 'name', 'xyz').length === 0
[key^=value] (Starts with)

Filtert darauf, dass das angegebene Attribut des Elements mit dem Wert beginnt:

t.cf['^='](document.getElementById('testfeld').getElementsByTagName('input'), 'name', 'radio').length === 2 t.cf['^='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 'xyz').length === 0
[key$=value] (Ends with)

Übergibt ein Array mit Nodes, deren angegebenes Attribut mit dem Wert endet:

t.cf['$='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 't').length === 2 t.cf['^='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 'xyz').length === 0
[key*=value] (Partly Equal)

Filtert auf jene Nodes, deren Attributwert an einer beliebigen Stelle dem angegebenen Wert entspricht:

t.cf['*='](document.getElementById('testfeld').getElementsByTagName('input'), 'name', 'i').length === 4 t.cf['*='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 'xyz123').length === 0
[key~=value] (Seperate Part Equals)

Läßt nur solche Nodes übrig, in deren Wert des angegeben Attributs der übergebene Wert einzeln steht:

t.cf['~='](document.getElementsByTagName('h5'), 'title', 'nicht').length === 2 t.cf['~='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 'hidden').length === 0
[key|=value] (Subpart before "-" Equals)

Gibt jene Nodes zurück, deren Attributwert vom Start ab bis zum Ende oder vor einem "-" den Wert enthält:

t.cf['|='](document.getElementsByTagName('*'), 'lang', 'de').length === 1 t.cf['|='](document.getElementById('testfeld').getElementsByTagName('input'), 'value', 'xyz123').length === 0

Pseudo Attributes

:odd

Reduziert die Auswahl auf die Nodes mit ungradem Index (1, 3, 5, ...):

t.cf['odd'](document.getElementById('testfeld').getElementsByTagName('li')).length === 4 t.cf['odd'](document.getElementById('testfeld').getElementsByTagName('body')).length === 0
:even

Gibt nur Nodes mit geradem Index (2, 4, 6, ...) wieder:

t.cf['even'](document.getElementById('testfeld').getElementsByTagName('li')).length === 3 t.cf['even'](document.getElementById('testfeld').getElementsByTagName('body')).length === 0
:empty

Läßt nur Nodes ohne childNodes übrig:

t.cf['empty'](document.getElementById('testfeld').getElementsByTagName('*')).length === 8 t.cf['empty'](document.getElementById('testfeld').getElementsByTagName('ul')).length === 0
:first-child

Prüft, ob die angegebenen Nodes jeweils das erste HTML-Knoten-Kind ihres Eltern-Elements sind:

t.cf['first-child'](document.getElementById('testfeld').getElementsByTagName('*')).length === 9 t.cf['first-child'](document.getElementById('testfeld').getElementsByTagName('ul')).length === 0
:last-child

Filtert auf Nodes, die jeweils das letzte HTML-Knoten-Kind ihres Eltern-Elements sind:

t.cf['last-child'](document.getElementById('testfeld').getElementsByTagName('*')).length === 9 t.cf['last-child'](document.getElementById('testfeld').getElementsByTagName('ul')).length === 0
:only-child

Liefert alle Nodes zurück, die das einzige HTML-Knoten-Kind ihres Eltern-Elements sind:

t.cf['only-child'](document.getElementById('testfeld').getElementsByTagName('*')).length === 9 t.cf['only-child'](document.getElementById('testfeld').getElementsByTagName('ul')).length === 0
:nth-child(index)

Sortiert Nodes aus, die nicht das n-te HTML-Knoten-Kind ihres Eltern-Elements sind:

t.cf['nth-child'](document.getElementById('testfeld').getElementsByTagName('li'), "3").length === 1 t.cf['nth-child'](document.getElementById('testfeld').getElementsByTagName('li'), "4").length === 0
:not(selector)

Entfernt jene Nodes aus der Auswahl, auf die der Selektor zutrifft:

t.cf['not'](document.getElementById('testfeld').getElementsByTagName('li'), ".first").length === 5 t.cf['not']([document.getElementById('testfeld')], "div").length === 0
:has(selector)

Filtert alle Nodes, die Parent-Elemente der Nodes sind, auf die der Selektor zutrifft:

t.cf['has'](document.getElementById('testfeld').getElementsByTagName('*'), "li").length === 3 t.cf['has']([document.getElementById('testfeld')], "table").length === 0
:contains(text)

Gibt alle Nodes zurück, in deren Text-Inhalt irgendwo der angegebene Text steht:

t.cf['contains'](document.getElementById('testfeld').getElementsByTagName('*'), 'oben').length === 5 t.cf['contains']([document.getElementById('testfeld')], 'blargl').length === 0
:lang(language)

Testet die Nodes auf ihre Sprache, sofern angegeben:

t.cf['lang']([document.getElementById('testfeld')], 'de').length === 1 t.cf['lang']([document.getElementById('testfeld')], 'jp').length === 0

Erweiterungen der Selektoren

Das entsprechende Objekt kann leicht um weitere Selektoren erweitert werden. Zusätzliche Pseudoattribute kann man durch ihren Namen ohne vorangestellten ":" definieren. Selektor-Funktionen bekommen ein Array mit den zu filternden Nodes und ggf. einen zusätzlichen Parameter als String geliefert.

t._ - Selektions-Methoden

Selektierte DOM-Nodes werden mit den folgenden Methoden erweitert:

t._.i - Iterate

Führt eine Funktion aus, innerhalb der this das Selektions-Objekt, der erste Parameter der Index und der zweite Parameter das jeweilige Node ist.

t('#testfeld, #top').i(function(i, n) { ... });

t._.f - Filter

Filtert die Nodes der Selektion anhand einer Funktion, die this als Selektions-Objekt und als Parameter Index und Node übergeben bekommt.

t('#testfeld li').f(function(i, n) { return i===3||i===5?n:undef; })

t._.g - Get

Vielseitige Methode, um mehr aus einer Selektion zu holen. Wenn ein Index (Integer) übergeben wird, bekommt man das entsprechende Node samt Methoden zurück. Wird stattdessen ein Node übergeben, bekommt man dessen Index. Ein String, der einen weiteren Selektor enthält, verwendet die bestehende Collection als Basis.

t('#testfeld li').g(1), t('h1').g(t('#plugins')[0]), t('body').g('#testfeld')

t._.e - Events

Belegt die selektierten Nodes mit Events. Siehe auch t.e, der erste Parameter ist jedoch hierbei schon mit der Selektion gesetzt.

t._.a - Attributes

Setzt Attribute der bestehenden Nodes. Wenn kein Attributwert angegeben ist, wird das Attribut aus dem ersten Node der Collection ausgelesen. Die Normalisierung findet über die Normalisierungs-Filter statt.

t._.c - CSS Attributes

Setzt Style-Attribute der bestehenden Nodes. Wenn kein Attributwert angegeben ist, wird das Attribut aus dem ersten Node der Collection ausgelesen. Die Normalisierung findet über die Normalisierungs-Filter statt.

t('#top').c('backgroundColor')==='#000000'

t._.h - HTML

Liest oder schreibt den HTML-Inhalt. In manchen Browsern (Safari) ist dieser Inhalt nicht sofort verfügbar.

t('#testfeld h1 a').h() === 'nach oben' --value test: failed

t._.v - Values

Liest oder schreibt den Wert eines Input-Feldes. Wenn t.p als Parameter übergeben wird, kann damit eine komplette Formular-Selektion parameterisiert werden.

t('#testfeld input[type=hidden]').v() == 'versteckt'

t._.r - Remove Element

Löscht alle selektierten Nodes.

t(node).r()

t._.nf - Normalisation filters

Die Normalisierungs-Funktionen sind in einem eigenen Objekt abgelegt, so dass sie leicht erweitert werden können. Die Notwendigkeit der Normalisierung wird ausschließlich durch Feature-Erkennung ermittelt.

t.e - Events

Einfache Event Engine:

t.e([node(s):DOM Node|Array], [eventname:String, optional], [callback:Function, this=node, arguments=[event object], optional]);

Wenn das Callback weggelassen wird, gibt die Funktion das Array des Events "eventname" zurück. Ist auch dieser nicht angegeben, bekommt man das gesamte Event-Objekt zurück.

t.e(document, 'ready', function() { t('#domready').a('class', 'passed')[0].innerHTML='passed'; }); -- passed after document.ready: failed

Die Events sind insofern normalisiert, dass ein Tastencode in der Eigenschaft "key" steht, die normalisierte Cursorposition in mouseX und mouseY und immer eine target-Eigenschaft vorhanden ist.

Helpers

t.i - Iterate

Interiert über Arrays und Objekte:

t.i([object:Array|Object], [callback:Function, this=object, arguments=[key, value]]);
t.i([1, 2, 3], function(i, v) { console.log(v); });
t.i(window, function(k, v) { console.log(k); });

t.x - eXtend Object

Erweitert Objekte und Arrays:

t.x([object:Object], [object2:Object], [object3:Object, optional], ...);

Objekte erweitern:

t.x({x:1}, {y:2}, {z:3}) => {x:1, y:2, z:3}

Arrays erweitern:

Arrays werden einfach aneinander gehängt:

t.x([1,2], [3,4,5], [6,7,8]); // -> [1,2,3,4,5,6,7,8]

t.f - Filtert Arrays und Objekte:

Gibt ein gefiltertes Array oder Objekt zurück. Wenn asArray true ist, wird das Objekt in jedem Fall als Array behandelt. Die Funktion hat in this das Objekt und bekommt die key-value-Paare als Parameter übergeben. Wenn sie "undefined" zurückgibt, wird die Instanz ausgefiltert. Wird nur ein Array und nichts weiteres übergeben, dient t.f als "unique"-Funktion, d.h. sie filtert doppelte Einträge aus.

t.f([Object/Array], [Function], [optional: asArray])> t.f([1,1,1,2,1,1,3,1]) => [1,2,3]

t.w - Where in the Array is...?

Gibt den Index eines Objekts im Array oder -1 zurück

t.w([object], [array:Array]) t.w(3, [1,2,3,4,5]) === 2
t.w([object], [array:Array])

t.p - Parameterize Object

Verwandelt ein Objekt in einen parameterisierten String:

t.p([object:Object], [middle:String, optional], [connector:String, optional], [prefix:String, optional], [suffix:String, optional], [filter1:Function, optional], [filter2:Function, optional]) t.p({test:'hat',wunderbar:'geklappt'}) === 'test=hat&wunderbar=geklappt'

Wenn man t.p als Parameter an die Selektions-Methode v übergibt, bekommt man die Values aller selektierten Nodes als URL-Parameterierten String zurück.

Network

t.a - Ajax

Vielseitiger Ajax Request:

t.a({
    url:[url:String],
    method:[method:String,(GET|POST), optional],
    data:[postdata:String|Object, optional],
    async:[callback:Function, optional]
});

t.j - JavaScript, JSONp

Kann Scripte (etwa Plugins) einfügen oder JSONp-Requests durchführen:

t.j([url:String], [callback:(name)String, Function], [timeout,Integer(ms, optional)]); // lädt Script od. JSONp-Request

Plugins

Code-Styleguide und Minifizierung

Das Ziel der Größenreduzierung hat zwar oberste Priorität, dennoch sollte ein gewisser Stil nicht fehlen und Geschwindigkeitsoptimierungen, die mit wenigen Zeichen zu erzielen sind, nicht verschmäht werden. In den Situationen, in denen die Reduzierung die Lesbarkeit zu sehr beeinträchtigt, wird zugunsten der Lesbarkeit entschieden oder so weit dokumentiert, dass auch ein Unbeteiligter noch versteht, was passiert. Stellen, an denen Debugging interessant sein können, werden so mit Umbrüchen getrennt, dass sie im Debugger noch zu erfassen sind. Des weiteren ist in der Entwicklerversion jede Zeile, die nicht selbsterklärend ist, mit einem Kommentar zu versehen. Lieber einen Kommentar zu viel als zu wenig.

Die Minifizierung sollte nur solche Umbrüche entfernen, die das Debugging nicht gefährden - wenn mehr als eine problematische Anweisung in einer Zeile steht, muss man im Zweifel raten - was die Nützlichkeit der Minifizierung leider mindert.