
Witam w kontynuacji poprzedniego artykułu – Kurs Prototype JS w pigułce. Dziś skupimy się na elementach dokumentu i wykonywaniu na nich operacji.
Krok dalej we framework Prototype JS
W tej części kursu zobaczymy pracę z DOM w wydaniu Prototype JS. Przejdźmy zatem od razu do omawiania kolejnych zagadnień tej biblioteki.
Obiekt Template
Pozwala nam uzyskać coś w rodzaju interpolacji; w JavaScript jest to wygodne narzędzie wspomagające prezentację danych, np. po ich otrzymaniu z sieci i przetworzeniu.
Przykład – użycie obiektu Template:
var cart = new Object(); cart.items = []; cart.items.push({ product: 'Book', price: 24.50, quantity: 1 }); cart.items.push({ product: 'Pencils', price: 5.44, quantity: 3 }); var itemFormat = new Template ( 'You are ordering #{quantity} units ' + 'of #{product} at $#{price} each' ); var formatted = ''; for (var i = 0; i < cart.items.length; i++) { var cartItem = cart.items[i]; formatted += itemFormat.evaluate(cartItem) + '<br/>\n'; }
Po bliższych oględzinach zauważymy jak fajny jest to przykład możliwości. Definiujemy format, w jakim będą wyświetlane dane (zmienna itemFormat) a następnie iterujemy po zdefiniowanych danych (w kolekcji items) aby wydobyć dane, które zostaną podstawione w odpowiednich miejscach.
Przykład 2 – użycie Template do budowania elementów URL:
var hx = new Template('show.php?lang=#{lang}&cat=#{mycat}&x=#{x}'); var selection = { my_cat: 'books' , lang: 'en' }; hx.evaluate(selection); // 'show.php?lang=en&cat=books&x='
W obiekcie selection brakuje klucza „x” – po prostu zostanie pominięty podczas przetwarzania szablonu, więc nie zostanie podstawiona żadna wartość w „&x=”.
Inne spojrzenie na tablice
Poznaliśmy już obsługę tablic w „czystym” JavaScript. Prototype JS daje nam dodatkowe możliwości obsługi tablic, tudzież upraszcza standardowe metody.
Zacznijmy od iteracji.
Implementacja wzorca iterator, służy w uproszczeniu do przechodzenia po kolejnych elementach (zbioru, obiektu czy też tablicy).
Przykład – iterowanie z użyciem each():
var myArray = new Array('A', 'B', 'C', 'D'); myArray.each(function(item) { // kod pracujący na poszczególnym (bieżącym) elemencie alert(item); }); // Przypadek 2: iteracja po elementach HTML: //var elements = $$('.menu-item'); //elements.each(function(element) { // ... //});
Metody związane z Array dostarczane przez Prototype JS
– clear() – kasuje zawartość tablicy
– clone() – klonowanie tablicy
var two = mytab.clone(); // 'mytab' zostanie sklonowana w 'two'
– compact() – zwraca nową wersję tablicy, bez wartości null / undefined
– each() – iterator
– first() – zwraca pierwszy element tablicy
– flatten() – transformacja na tablice 1-D , np.:
['al', ['bob', 'lisa']].flatten(); // ['al', 'bob', 'lisa']
– from() – alias metody $A()
– indexOf() – indeks elementu
– inspect() – zwraca string zorientowany na debug
– last() – ostatni element, przeciwieństwo first()
– reverse() – odwracanie
– size() – rozmiar, podobnie jak natywny .length
– toArray() – konwersja na tablicę, np.:
var str = 'a, b, c'; alert(string.toArray()); alert($R(1, 5).toArray()); // [1, 2, 3, 4, 5]
– toJSON() – przekształcenie na JSON, np.:
['a', {b: null}].toJSON(); // '["a", {"b": null}]'
– uniq() – usuwa duplikaty z tablicy
– without() – zwraca tablicę z pominięciem określonych wartości, np.:
[5, 6, 1, 20].without(20, 6) // -> [5, 1]
Class i programowanie obiektowe OOP z pomocą Prototype JS
Omawialiśmy już OOP w JavaScript, Prototype JS daje nam kolejne możliwości.
Operacje na „klasach” mogą odbywać się według schematu jak w poniższym przykładzie:
var Klasa = {}; // ... // addMethods - dodanie metod Klasa.addMethods({ speak: function() { // alert(this.name); alert('Hello!'); } });
Tworzenie „klasy” (warstwa z Prototype JS) – służy do tego metoda create():
create([superclass][, methods…]);
Parametr superclass określa klasę nadrzędną, natomiast do rodzica odwołujemy się poprzez $super, przez co dość łatwo uzyskujemy możliwość implementacji dziedziczenia.
Przykład – użycie Class.create():
var Animal = Class.create({ initialize: function(name, sound) { this.name = name; this.sound = sound; }, speak: function() { alert(this.name + " says: " + this.sound + "!"); } }); // "wyprowadzamy" zwierzę z klasy Animal: var Snake = Class.create(Animal, { initialize: function($super, name) { $super(name, 'hissssssssss'); } }); var ms = new Snake("Python"); ms.speak(); // "Python says: hissssssssss!" var snake2 = new Snake("Boa"); snake2.speak(); // "Boa says: hissssssssss!" // wbudujmy tu jeszcze Enumerable var AnimalPen = Class.create(Enumerable, { initialize: function() { var args = $A(arguments); if (!args.all(function(arg) { return arg instanceof Animal })) throw "Only animals in here!" this.animals = args; }, // implementacja _each aby używać metod z Enumerable _each: function(iterator) { return this.animals._each(iterator); } }); var snakePen = new AnimalPen(ms, snake2); snakePen.invoke('speak'); // invoke - wywołanie metody // alert: "Python says: hissssssssss!" // alert: "Boa says: hissssssssss!"
Mnie osobiście takie podejście do OOP w JavaScript, wspomagane przez framework Prototype JS, jak najbardziej odpowiada.
Zajmijmy się teraz elementami dokumentu i zobaczmy jakie ciekawe narzędzia daje nam Prototype JS.
Obiekt Element – głębsze spojrzenie
Obiekt element zaprezentowany został już w pierwszej części kursu. Teraz rozwiniemy ten temat.
A na początek Element jako konstruktor.
Przykład:
var a = new Element('a', { 'class': 'foo', href: '/file.htm' }) .update("Next page");
Zostanie stworzony odnośnik na bazie podanych parametrów.
Krok dalej: Element, Prototype JS i rozszerzanie DOM poprzez metodę extend().
Przykład – dynamiczne tworzenie elementu:
var my_div = document.createElement('div'); Element.extend(my_div); my_div.addClassName('pending').hide(); document.body.appendChild(my_div);
Tip: wskazówka cross-browser dla starszych wersji:
$('someElement').parentNode.hide(); // błąd w IE $($('someElement').parentNode).hide(); // cross-browser
Metody z obiektu Element:
– absolutize() – nadaje position absolute
– addClassName() – dodanie klasy, np.:
$('divek').addClassName('klasa');
– addMethods() – addMethods([methods]) lub addMethods(tagName, methods), przykładowo:
var MyUtils = { truncate: function(element, len) { element = $(element); return element.update(element.innerHTML.truncate(len)); }, updateAndMark: function(element, html) { return $(element).update(html).addClassName('updated'); } } Element.addMethods(MyUtils); // try it: $('explanation').truncate(100);
– ancestors() – pobiera przodków elementu, np.:
<div id="father"><div id="kid"></div></div>
$('kid').ancestors(); // wynik: [div#father, body, html]
– childElements() – dzieci elementu, depth = 1
– classNames() – zwraca tablicę z klasami elementu, np.:
$('divek').classNames(); // ['apple', 'fruit', 'food']
– cleanWhitespace() – czyści białe znaki
– clonePosition() – clonePosition(element, source[, options]), zwraca nowy HTMLElement
– cumulativeOffset() – skumulowana wartość offsetu elementu – x, y
– cumulativeScrollOffset() – offset w przypadku przewijania (zagnieżdżone elementy)
– descendantOf() – czy element jest potomkiem danego przodka, np.:
$('divek1').descendantOf('divek-foobar'); // true - 'divek' 1 jest potomkiem (zawiera sie w)
– descendants() – zwraca potomków jako tablicę
– down() – następny w dól, np.:
$(element).down(1).next('li', 2).hide();
– empty() – sprawdza czy element jest pusty, dla samych whitespaces da true
– extend() – extend(element) – rozszerza element o metody z Element.Methods oraz Element.Methods.Simulated
– fire() – tworzy niestandardowe zdarzenia o podanej nazwie, a następnie uruchamia go w danym elemencie
– firstDescendant() – tzw. pierwszy potomek, na przykład:
<div id="australopithecus"> <div id="homo-erectus"><!-- This is cool --> <div id="homo-neanderthalensis"></div> <div id="homo-sapiens"></div> </div> </div>
$('australopithecus').firstDescendant(); // div#homo-erectus $('homo-erectus').firstChild; // comment node "This is cool" $('homo-erectus').firstDescendant(); // div#homo-neanderthalensis
– getDimensions() – rozmiary – wartości width, height, np.:
$('rectangle').getDimensions(); // {width: 200, height: 100}
– getElementsByClassName() – elementy z daną klasą:
$('divek').getElementsByClassName('nazwa');
– getElementsBySelector() – wybiera elementy po selektorze, przykładowo wg zadanej wartości dla title:
<li id="golden-delicious" title="yummy!">Golden Delicious<li>
$('apples').getElementsBySelector('[title="yummy!"]');
– getHeight() – pobiera wartość height (wysokość) elementu
– getOffsetParent() – element najbliższego przodka
– getStyle() – pobieranie aktualnego stylu elementu:
$(element).getStyle('font-size');
– getWidth() – pobiera width (szerokość) elementu
– hasClassName() – czy element ma klasę, np.:
$('divek').hasClassName('lol');
– hide() – ukryj element
– identify() – zwraca atrybut ID elementu (jeśli istnieje)
– immediateDescendants() – bezpośredni potomkowie
– insert() – dodawanie nowego elementu; insert(element, { position: content })
lub insert(element, content)
– inspect() – łańcuch zorientowany na debug (więcej informacji)
– makeClipping() – wykonuje kadrowanie elementu, np. div’a z obrazkiem:
$('zdjecie').makeClipping().setStyle({ width: '100px', height: '100px' });
– makePositioned() – pozycja relative
– match() – sprawdza dopasowanie: match(element, selector), np.:
<ul id="fruits"> <li id="apples"> <ul> <li id="golden-delicious">Golden Delicious</li> <li id="mcintosh">McIntosh</li> </ul> </li> </ul>
$('fruits').match('ul'); // true $('macintosh').match('li#macintosh.yummy'); // true $('fruits').match('p'); // false
– next() – następny węzeł równorzędny (rodzeństwo)
– nextSiblings() – następne węzły równorzędne (rodzeństwo)
– observe() – obserwacja obiektu / elementu czyli obsługa zdarzeń, przykładowo:
$(element).observe('click', function(event) { alert(Event.element(event).innerHTML); });
– positionedOffset() – offsetLeft i offsetTop
– previous() – poprzedni węzeł (rodzeństwo)
– previousSiblings() – jak wyżej, ale wszystkie poprzednie węzły
– readAttribute() – odczyt atrybutu, np.:
$('mylink').readAttribute('href'); // '/tags/prototype'
– recursivelyCollect(property) – rekursywne zbieranie elementów których stosunek do danego elementu jest określony przez właściwość (property), np.:
$('my-ul-li-element').recursivelyCollect('firstChild');
– relativize() – czyni element pozycjonowanym względnie
– remove() – usuwanie, np.:
// usuwa element z dokumentu (i ewentualnie zwraca go): var x = $('divek').remove();
– removeClassName() – usuwa klasę CSS, np.:
$('divek').removeClassName('klasa');
– replace() – podmiana elementu, np.:
replace(element[, html]); // parametr html określa nową zawartość, która może zostać dodana
– scrollTo() – przewija do danego elementu, np.:
$(element).scrollTo();
– select() – wybiera element
– setOpacity() – manipulacja przeźroczystością
// 50% transparency - ustawia 50% przezroczystości $(element).setOpacity(0.5);
– setStyle() – dynamiczne ustawienie stylu, np.:
$(element).setStyle({ backgroundColor: '#900', fontSize: '12px' });
– show() – pokaż element (jeśli ukryty), patrz: hide()
– siblings() – tablica sąsiadów, np.: $(‚divek’).siblings();
– stopObserving() – przestań obserwować / obsługiwać zdarzenie; patrz: observe()
Przykład:
$(element).stopObserving('click', coolAction); // unregister coolAction
– toggle() – zmienia widoczność elementu, wywoła show() gdy ukryty, hide() gdy widoczny:
$('el').toggle();
– toggleClassName() – podobnie jw. – dodaje / usuwa klasę, jak mamy np. class=”foo bar”;
elem.toggleClassName("foo"); // class="bar";
– undoClipping() – odwrotna do makeClipping
– undoPositioned()– przywraca poprzednią pozycję dla elementu
– up() – zwraca pierwszego przodka elementu
– update() – aktualizacja zawartości elementu
– viewportOffset() – zwraca współrzędne x, y
– visible() – czy element jest widoczny
$('visible').visible(); // true lub false
– wrap() – dodaje wrapper do elementu / obudowuje go innym elementem), na przykład:
var div = new Element('div', { 'class': 'table-wrapper' }); $('elem').wrap(div);
Podsumowanie
Tym spisem metod kończymy część 2. kursu Prototype JS. Zapraszamy do podsumowującej części 3. Będzie to część ostatnia, ale tylko podstawowego kursu Prototype JS, nie artykułów związanych z tą biblioteką.
Samych ciekawych narzędzi i ułatwień, które zbudowano w oparciu o nią i są godne polecenia, jest całkiem sporo.
Dziękuję za uwagę.