W poprzednich częściach kursu nauczyliśmy się poruszać po drzewie dokumentu, tworzyć nowe elementy strony oraz zmieniać ich cechy, odczytywać je oraz przechowywać dodatkowe informacje w elementach. Przyszła pora poznać kilka metod dokonujących znaczących zmian w strukturze dokumentu - dwie z nich poznaliśmy wcześniej - adopt i clone, teraz nauczymy się kilku nowych.

Czy jesteś paragrafem ?

Wiemy, że typ elementu można sprawdzić za pomocą zapisu:

element.get('tag');

Gdybyśmy jednak potrzebowali sprawdzić czy dany element jest określonego typu (przykładowo paragrafem) to zamiast pisać:

if(element.get('tag') == 'p'){
    // kod wykonywany po spełnieniu warunku
}

Możemy skorzystać z metody match, która przyjmuje jako argument typ elementu i zwraca wartość true, jeżeli dany element jest właśnie takiego typu:

if(element.match('p')){
    // kod wykonywany po spełnieniu warunku
}

Przy okazji zapis warunku jest trochę krótszy.

Zmiany zawartości elementu

Wcześniej poznaliśmy też właściwości text i html, które pozwalały manipulować zawartością elementu. Aby wyczyścić element z wszelkich elementów i teksów należało zapisać:

element.set('html','');

Ale warto wiedzieć, że można to zapisać krócej za pomocą metody empty, która robi to samo co poprzedni zapis:

element.empty();

Możemy też dodawać do elementu tekst (i wokół niego) nie tylko za pomocą zapisu:

element.set('text', element.get('text')+'Nowy tekst');

ale także za pomocą o wiele prostszego zapisu z użyciem metody appendText o następującej składni:

element.appendText('tekstDoDodania', 'pozycja');

Parametr pozycja jest opcjonalny i przyjmuje jedną z czterech wartości: top, bottom, before, after. Wartość bottom jest wartością domyślną.

Najlepiej umiejscowienie tekstu po użyciu tych parametrów zademonstruje poniższy schematyczny kod:

// BEFORE
<div id="test">
    // TOP
    <p>Jakiś tekst</p>
    // BOTTOM
</div>
// AFTER

Po wywołaniu metody appendText:

$('test').appendText('Testowy tekst', pozycja);

Ze zmienną pozycja mającą jedną ze wspomnianych wcześniej wartości tekst zostanie dodany w miejsce odpowiedniego komentarza.

Z elementami jest podobnie

Metoda inject działa podobnie jak metoda appendText z tą różnicą, że operuje na całych elementach. Druga różnica jest taka, że pierwszym argumentem tej metody jest punkt odniesienia dla pozycji, zatem zapis:

element1.inject(element2, 'top');

Spowoduje umieszczenie element1 na samym początku element2. Na bazie tej metody i kilku wcześniej poznanych można zbudować całkiem szybko listę sortowalnych elementów.

Przyjmijmy, że mamy listę nieuporządkowaną, która zawiera kilka pozycji z różnymi tekstami. Każda z tych pozycji jest wyposażona w przyciski 'przesuń w górę' i 'przesuń w dół' - oczywiście krańcowe elementy są pozbawione jednego przycisku (jest on ukrywany w sposób dynamiczny). Niech nasza lista posiada atrybut id o wartości "dynamicList".

Każda pozycja listy będzie miała następującą budowę:

<li>
      Tekst
      <img src="up.png" alt="Przesuń w górę" />
      <img src="down.png" alt="Przesuń w dół" />
</li>

Do przemieszczania elementów wystarczy poniższy kod:

window.addEvent("domready", function(){
    $('dynamicList').getChildren().each(function(el){
        el.getFirst('img').addEvent("click", function(){
            el.inject(el.getPrevious(), 'before');
        });
 
        el.getLast('img').addEvent("click", function(){
            el.inject(el.getNext(), 'after');
        });
    });
});

Dla każdego elementu listy dokonujemy selekcji obu przycisków i dodajemy do nich zdarzenia zawierające metody inject. Dla przycisku "Przesuń w górę" wykonujemy kod dokonujący wstawienia danego elementu przed poprzednim elementem, w wypadku drugiego przycisku wstawiamy element po następującym po nim elemencie.

Małą wada jest fakt, że krańcowe elementy nie mają ukrytych przycisków. Oczywiście można by ukrywać przycisk przy kliknięciu elementu w przesuwanych elementach i pokazywać potrzebne przyciski, które były ukryte, ale o wiele łatwiej jest stworzyć funkcję, która ukrywa pierwszy i ostatni przycisk w obrębie całej listy:

function hideButtons(){
    $('dynamicList').getFirst().getFirst('img').setProperty('style','visibility:hidden');
    $('dynamicList').getLast().getLast('img').setProperty('style','visibility:hidden');
}

Należy jeszcze uwzględnić fakt, że pewne przyciski trzeba ponownie pokazać użytkownikowi - zamiast pokazywać profilaktycznie wszystkie przyciski wystarczy dodać naszej funkcji jeden argument - element w którym należy ponownie pokazać przyciski:

function hideButtons(el1, el2){
    if($defined(el1)){
        el1.getElements('img').setProperty('style','visibility:visible');
        el2.getElements('img').setProperty('style','visibility:visible');
    }
    $('dynamicList').getFirst().getFirst('img').setProperty('style','visibility:hidden');
    $('dynamicList').getLast().getLast('img').setProperty('style','visibility:hidden');
}

Warunek w pierwszej linijce funkcji jest potrzebny ponieważ musimy pamiętać, że trzeba dodatkowo ukryć przyciski przy inicjalizacji skryptu. Ostatecznie kod skryptu w całości wygląda następująco:

window.addEvent("domready", function(){
    $('dynamicList').getChildren().each(function(el){
        el.getFirst('img').addEvent("click", function(){
            el.inject(el.getPrevious(), 'before');
            hideButtons(el.getNext(), el);
        });
 
        el.getLast('img').addEvent("click", function(){
            el.inject(el.getNext(), 'after');
            hideButtons(el.getPrevious(), el);
        });
    });
 
    hideButtons();
});
 
 
function hideButtons(el1, el2){
    if($defined(el1)){
        el1.getElements('img').setProperty('style','visibility:visible');
        el2.getElements('img').setProperty('style','visibility:visible');
    }
    $('dynamicList').getFirst().getFirst('img').setProperty('style','visibility:hidden');
    $('dynamicList').getLast().getLast('img').setProperty('style','visibility:hidden');
}

Cały skrypt w akcji:

PRZYKŁAD 1

Usuwanie elementów

W jednej z poprzednich części kursu poznaliśmy metodę dispose, która pozwalała usunąć element z drzewa dokumentu. Istnieją jeszcze dwie inne metody, które pozwalają usuwać elementy - destroy i replaces. Metoda destroy różni się od metody dispose tym, że nie zwraca niczego, natomiast metoda dispose zwracała nam usuwany z drzewa dokumentu element, który mogliśmy ponownie wstawić do dokumentu, na przykład za pomocą metody inject. Z kolei metoda replaces usuwa dany element i zastępuje go innym :

nowyElement.replaces(staryElement);

Przy czym usuwanie w tej metodzie nie działa na zasadzie metody destroy:

staryElement.destroy();

Ale na zasadzie metody dispose - zatem zastępowany element możemy ponownie wstawić do dokumentu.

Wzbogacimy naszą listę o możliwość usuwania elementów - dodamy do struktury pozycji listy dodatkowy przycisk:

<li>
        Tekst
        <img src="up.png" alt="Przesuń w górę" />
        <img src="delete.png" alt="Usuń ten element" />
        <img src="down.png" alt="Przesuń w dół" />
</li>

Do jego obsługi wystarczy dodać poniższy kod w pętli dodającej zdarzenia do przycisków:

el.getChildren('img')[1].addEvent("click", function(){
      el.destroy();
      hideButtons();
});

Zatem cały kod prezentuje się teraz następująco:

window.addEvent("domready", function(){
    $('dynamicList').getChildren().each(function(el){
        el.getFirst('img').addEvent("click", function(){
            el.inject(el.getPrevious(), 'before');
            hideButtons(el.getNext(), el);
        });
 
        el.getLast('img').addEvent("click", function(){
            el.inject(el.getNext(), 'after');
            hideButtons(el.getPrevious(), el);
        });
 
        el.getChildren('img')[1].addEvent("click", function(){
            el.destroy();
            hideButtons();
        });
    });
 
    hideButtons();
});
 
function hideButtons(el1, el2){
    if($defined(el1)){
        el1.getElements('img').setProperty('style','visibility:visible');
        el2.getElements('img').setProperty('style','visibility:visible');
    }
    $('dynamicList').getFirst().getFirst('img').setProperty('style','visibility:hidden');
    $('dynamicList').getLast().getLast('img').setProperty('style','visibility:hidden');
}

Przykład po zmianach w akcji:

PRZYKŁAD 2

Otaczanie elementów

Na koniec opiszę dwie metody - grab i wraps. Metoda grab to odpowiednik metody adopt, który akceptuje tylko jeden element. Opcjonalnie możemy określić pozycję dodawanego elementu - top lub bottom:

rodzic.grab(potomek);

W poniższym wypadku element potomny zostanie umieszczony na samym początku elementu-rodzica:

rodzic.grab(potomek, 'top');

W tym wypadku element potomny zostanie umieszczony jako ostatni element potomny elementu-rodzica:

rodzic.grab(potomek, 'bottom');

Różnica pomiędzy działaniem metod grab i wraps jest dość subtelna, ale czasem może okazać się istotna - chodzi mianowicie o strukturę dokumentu po wykonaniu operacji.

Weźmy teraz pod uwagę następującą strukturę dokumentu:

<div id="test1">Test 1</div>
<div id="test2">Test 2</div>
<div id="test3">Test 3</div>
<div id="test4">Test 4</div>
<div id="test5">Test 5</div>

Jeżeli wykonamy operację:

$('test4').grab('test2');

Otrzymamy rezultat postaci:

<div id="test1">Test 1</div>
<div id="test3">Test 3</div>
<div id="test4">Test 4
    <div id="test2">Test 2</div>
</div>
<div id="test5">Test 5</div>

Gdybyśmy teraz wykonali to samo, ale z użyciem metody wraps na początkowo wspomnianej strukturze:

$('test4').wraps('test2');

To otrzymamy:

<div id="test1">Test 1</div>
<div id="test4">Test 4
    <div id="test2">Test 2</div>
</div>
<div id="test3">Test 3</div>
<div id="test5">Test 5</div>

Jak widać różnica pomiędzy tymi metodami polega na tym, że metoda wraps "owija" element rodzica wokół potomka, a metoda grab umieszcza potomka w elemencie rodzica. W rezultacie otrzymujemy dwa zupełnie inne rezultaty na stronie.

Dla treningu

Tym razem ciekawsze zadanie - proponuję ulepszyć naszą implementację listy tak by była ona wielopoziomowa - każdy z jej elementów może stać się elementem nadrzędnym osobnej listy. Aby uzyskać taki efekt warto zastanowić się nad użyciem metod grab i adopt. Zasada jest taka, że każdy element posiada dodatkowy przycisk do tworzenia podlisty (tworzenie podlisty polega na stworzeniu w danym elemencie kolejnej listy nieuporządkowanej tak by można się było do niej potem łatwo odwołać - tutaj można wykorzystać selektory lub metody set i get do przechowywania stosownych informacji). Dodatkowo pierwszy i ostatni element listy posiadałyby specjalne przyciski, które pozwalają umieścić je poziom wyżej z tym, że pierwszy element umieszczany by był przed listą z której pochodzi, a ostatni - za nią. Oczywiście należy pamiętać o ukrywaniu stosownych przycisków w określonym momencie - na przykład w momencie gdy dany element nie jest już pierwszym lub ostatnim elementem danej listy.

Podsumowanie

Poznaliśmy metody związane z manipulacją strukturą dokumentu i pozostało nam jeszcze zapoznać się z kilkoma metodami związanymi z klasami elementu oraz kilka metod, których nie można zaklasyfikować do jakiejś szerszej grupy funkcjonalności - to wszystko w kolejnej i ostatniej części kursu poświęconej zagadnieniom związanym z plikiem Element.js

Komentarze do wpisu "MooTools 1.2 - elementy strony cz. 3":

Jeszcze nie ma żadnych komentarzy. Twój może być pierwszy.

Dodaj komentarz:

Textile Lite włączony ( szczegółowy opis znaczników ):
*strong* | # lista numerowana | * lista wypunktowana | _em_ | __italic__ | "link":http:// | bq. cytat.