Антон Шевчук // Web-разработчик

Dojo Toolkit для начинающих // JavaScript

Dojo Toolkit

Возможно, многие уже читали статьи из серии jQuery для начинающих, да вот с некоторых пор меня заинтересовал еще один JavaScript фреймворк, и зовется он Dojo Toolkit.

В данной статье я постараюсь описать базовые возможности Dojo, так же буду проводить параллели с jQuery, так что не пугайтесь возникшему дежавю…

Как я уже когда-то говорил – учиться лучше на примерах, так что приступим…

Подключение

Ну, для начала Вам понадобится сам фреймворк, его вы сможете скачать с домашней страницы проекта, затем подключаем его одним из следующих способов:

Используя локальный файл:

<script type="text/javascript" src="js/dojo/dojo.js" djConfig="parseOnLoad:true, isDebug:true"></script>

Данная запись аналогична предыдущей:

<script type="text/javascript">
    var djConfig = {
        isDebug:true,
        parseOnLoad:true
    };
</script>
<script type="text/javascript" src="js/dojo/dojo.js"></script>

Dojo так же доступен на следующих хостах:

<!-- AOL -->
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.3/dojo/dojo.xd.js"></script>
<!-- Google -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></script>

Селекторы

Перейдем к поиску элементов, для этой цели есть следующие функции:

// самый простой вариант - получить елемент по его Id
var element  = dojo.byId('elementId');
// так же можем выбрать несколько элементов, используя селекторы из CSS3, - возвращается объект NodeList
var elements = dojo.query('.elementsClass')

Список поддерживаемых CSS селекторов можно найти в документации по функции dojo.query (docs.dojocampus.org).

События

Для работы с событиями в Dojo используются функции dojo.connect и dojo.disconnect – для добавления и удаления обработчиков соответственно, приведу простой пример:

// некая функция
function update() {
    console.log('click!');
}
// и некоторый элемент
var obj = dojo.byId('someId');

// вешаем обработчик
var link = dojo.connect(obj, "onclick", null, "update");
// или
    dojo.connect(obj, "onclick", "update");
// или используя анонимную функцию
    dojo.connect(obj, "onclick", function update() { console.log('click!'); });

// убираем наш обработчик
dojo.disconnect(link);

Функция connect так же поддерживается NodeList’ом:

// переделаем чуть-чуть предыдущий пример
dojo.query('.someClass').connect("onclick", function update() { console.log('click!'); });

Примечание: dojo одинаково понимает события click и onclick

Простые примеры

Теперь перейдем непосредственно к работе, начнем с события, которое оповестит нас о завершении строительства DOM’а:

// создаем функцию init и вызваем ее по событию OnLoad
var init = function(){
    console.log("DOM построен..."); 
};
dojo.addOnLoad(init);
    
// и/или используем анонимную функцию
dojo.addOnLoad(function(){
    console.log("...спасибо"); 
});

Тривиальная задача – организация “зебры” из некой таблицы:

// таблица c "id=tabular_data" берем ее "tbody" 
// (используем селектор ">" дабы выбрать лишь нужные элементы, исключая подтаблицы)
// и каждому нечетному элементу "tr" добавляем класс "odd"
dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");

Изменение атрибутов, классов и стилей объектов:

// получаем атрибут title
dojo.attr("Id/Node", "title"); // title="bar"

// устанавливаем атрибут title
dojo.attr("Id/Node", "title", "A new Title");

// устанавливаем несколько атрибутов
dojo.attr("Id/Node", {
  "tabindex": 2, // add to tab order
  "onclick": function(e) {
    // add a click event to this node
  }
});

// добавляем класс anewClass
dojo.addClass("Id/Node", "anewClass");

// удаляем класс anewClass
dojo.removeClass("Id/Node", "anewClass");

// переключатель класса (нет - добавит, есть - удалит)
dojo.toggleClass("Id/Node", "anewClass");

// работает так же и с NodeList (т.е. массивом элементов)
dojo.query(".selector").toggleClass("anewClass");

// вернет объект стиля
dojo.style("Id/Node");

// вернет значение opacity
dojo.style("Id/Node", "opacity");

// установит значение opacity=0.5
dojo.style("Id/Node", "opacity", 0.5);

// изменяем несколько свойств
dojo.style("Id/Node", {
    "opacity": 0.5,
    "border": "3px solid black",
    "height": "300px"
});

// работает так же и с NodeList (т.е. массивом элементов)
dojo.query(".selector").style({
    "opacity": 0.5,
    "border": "3px solid black",
    "height": "300px"
});

Примечание: если какая-либо функция требует указания node в качестве параметра объекта (см. dojo.animateProperty), значит она не сможет переварить NodeList, даже если там содержится лишь один объект, конечно, это логично, но после jQuery – немного напрягает

Выдвижная панель

Начнем с простенького примера – слайд-панель, она у нас будет двигаться вверх/вниз по клику на ссылке (см. пример)

Реализуем это следующим образом, по клику на ссылку, у нас будет переключаться её класс (между “active” и “btn-slide”), а панелька с id=”panel” будет выдвигаться/прятаться. (класс “active” изменяет позицию фонового изображения, см. CSS код).

dojo.addOnLoad(function(){
    dojo.query(".btn-slide").connect("onclick",function(e){
        dojo.stopEvent(e);
        var panel = dojo.byId('panel');
        if (dojo.style(panel, 'height') != 0) {
            dojo.anim(panel, {height:0}).play();
        } else {
            dojo.anim(panel, {height:200}).play();
        }
        dojo.toggleClass(this, "active");
    });
});

 

Магические исчезновения

Этот пример покажет, как можно красиво и легко убирать растворять элементы (см. пример):

Когда мы кликаем по картинке , будет найден родительский элемент

, и его прозрачность будет медленно изменяться от opacity= 1.0 до opacity=0 – для этого воспользуемся функцией dojo.fadeOut:

dojo.addOnLoad(function(){
    dojo.query(".pane .delete").connect("onclick",function(){
        dojo.fadeOut({node:this.parentNode, duration:1000, onEnd: dojo.partial(dojo.style, this.parentNode, "display", "none")}).play();
        // по завершению - прячем элемент display:none
    });
});

Связанная анимация #1

Теперь пример посложнее, но он поможет Вам лучше понять Dojo. Всего несколько строк кода заставят квадраты двигаться, изменять размер и прозрачность. (см. пример):

Dojo Chainable Effects

// когда прогрузилась страница (DOM готов к манипуляциям)
dojo.addOnLoad(function(){
    // привязываемся к событию click для элемента с class="down"
    dojo.query(".down").connect("onclick",function(e){
        e.preventDefault(); // удаляем событие по умолчанию
        // бежим по всем найденым элементам с class="box"
        dojo.query('.box').forEach(function(el){
           dojo.anim(el,{    top: dojo.style(el,'top')+160    // наращиваем позицию top на 160px
                           , left: dojo.style(el,'left')+160         // наращиваем позицию left на 160px
                           , width: dojo.style(el,'width')+10     // наращиваем ширину на 10px
                           , height: dojo.style(el,'height')+10   // наращиваем высоту на 10px
                           , opacity: dojo.style(el,'opacity')-0.2   // уменьшаем opacity на 0.2
                           }, 1000).play(); // запускаем анимацию, указывая время в 1000ms = 1s
           });
        });
    });

    // привязываемся к событию click для элемента с class="up", далее все аналогично 
    dojo.query(".up").connect("onclick",function(e){
        e.preventDefault();
        dojo.query('.box').forEach(function(el){
           dojo.anim(el,{    top: dojo.style(el,'top')-160
                           , left: dojo.style(el,'left')-160
                           , width: dojo.style(el,'width')-10
                           , height: dojo.style(el,'height')-10
                           , opacity: dojo.style(el,'opacity')+0.2}, 1000).play();
           });
        });
    });
});

Примечание: в Opera 9.63 неправильно определяется первоначальное положение элементов

Связанная анимация #2

А теперь будем анимировать каждый box по отдельности. Для движения “вниз” будем использовать dojo.fx.chain – и вся анимация будет выполняться последовательно. Для движения вверх будем использовать dojo.fx.combine – анимация будет происходить параллельно (см. пример):

dojo.require("dojo.fx");
dojo.addOnLoad(function(){
    // выбираем каждый элемент по отдельности
    var box1 = dojo.query('.box:nth-child(1)')[0];
    var box2 = dojo.query('.box:nth-child(2)')[0];
    var box3 = dojo.query('.box:nth-child(3)')[0];
    
    // вешаем обработчик на "down"
    dojo.query(".down").connect("onclick",function(e){
        e.preventDefault();
        dojo.fx.chain(
        [
             dojo.animateProperty({node:box1,properties:{top: dojo.style(box1,'top')+160,duration:1000}}) // изменяем позицию первого квадрата
            ,dojo.animateProperty({node:box1,properties:{left: dojo.style(box1,'left')+160,duration:1000}})
            ,dojo.animateProperty({node:box2,properties:{top: dojo.style(box2,'top')+160,duration:1000}}) // изменяем позицию второго квадрата
            ,dojo.animateProperty({node:box2,properties:{left: dojo.style(box2,'left')+160,duration:1000}})
            ,dojo.animateProperty({node:box3,properties:{top: dojo.style(box3,'top')+160,duration:1000}}) // изменяем позицию третьего квадрата
            ,dojo.animateProperty({node:box3,properties:{left: dojo.style(box3,'left')+160,duration:1000}})
        ]
    ).play();
    });

    // вешаем обработчик на "up"
    dojo.query(".up").connect("onclick",function(e){
        e.preventDefault();
        dojo.fx.combine(
        [
             dojo.animateProperty({node:box1,properties:{top: dojo.style(box1,'top')-160,duration:1000}})
            ,dojo.animateProperty({node:box1,properties:{left: dojo.style(box1,'left')-160,duration:1000}})
            ,dojo.animateProperty({node:box2,properties:{top: dojo.style(box2,'top')-160,duration:1000}})
            ,dojo.animateProperty({node:box2,properties:{left: dojo.style(box2,'left')-160,duration:1000}})
            ,dojo.animateProperty({node:box3,properties:{top: dojo.style(box3,'top')-160,duration:1000}})
            ,dojo.animateProperty({node:box3,properties:{left: dojo.style(box3,'left')-160,duration:1000}})
        ]
    ).play();
    });
});

Примечание: функции dojo.fx.chain и dojo.fx.combine – работают с dojo.animateProperty и не понимают dojo.anim

Гармошка #1

Пример реализации “гармошки”. (см. пример)

Dojo Accordion

Теперь приступим к разбору полетов:

// подключаем dojo.fx и dojo.NodeList-fx
dojo.require("dojo.fx");
dojo.require("dojo.NodeList-fx");
dojo.addOnLoad(function(){

    // прячем все параграфы (можете использовать CSS, но будет не так интересно)
    dojo.query(".accordion div p").style({ display:'none' });

    // вешаемся на событие onclick
    dojo.query(".accordion h3").connect("onclick",function(){
        // получаем индекс текущего элемента в предке
        var index = dojo.query(".accordion h3").indexOf(this);

        // прячем все параграфы кроме текущего
        dojo.forEach(dojo.query(".accordion div").query("p"), function(item, idx){ 
                if (idx != index && dojo.style(item, 'display') != 'none') {
                    dojo.query(item).wipeOut().play();
                }
            });

        // получаем необходимый нам <p>
        var p = dojo.query("p", this.parentNode);

        // проверяем наличие класса active
        if (dojo.hasClass(this,'active')) {
            dojo.removeClass(this,'active'); // удаляем класс active
            p.wipeOut().play(); // прячем  <p>
        } else {
            dojo.addClass(this,'active'); // добавляем класс active
            p.wipeIn().play(); // показываем  <p>
        }
    });

});

Приведу сразу код HTML, чтобы далеко не ходить:

<div class="accordion">
<div>
    <h3>Question One Sample Text</h3>
    <p>Lorem ipsum dolor sit amet...</p>
</div>
<div>
    <h3>This is Question Two</h3>
    <p>Lorem ipsum dolor sit amet...</p>
</div>
<div>
    <h3>Another Questio here</h3>
    <p>Lorem ipsum dolor sit amet...</p>
</div>
</div>

Примечание: наткнулся на различное поведение селектора :first-child в Dojo и jQuery в приведенном примере. Dojo не находит элемент <p>, т.к. он не является first-child’ом относительно парента, jQuery же находит, т.к. он является первым потомком <p>. Кто прав я не могу сказать точно, но браузеры считают, что таки Dojo… (селектор :nth-child в Dojo так же считает вхождения потомков иначе).

Еще брошу камень в огород Dojo – кода в jQuery значительно меньше, и структура документа проще (хотя, возможно, я просто не знаю, как получить в Dojo следующий элемент в доме, имея лишь текущий node).

Так же не совсем понятно, почему при переборе dojo.query(".accordion div p").forEach(...) элементы идут в обратном порядке (лишь в webkit’е правильно), при этом dojo.query(".accordion div").query("p").forEach(...) работает верно во всех браузерах.

Гармошка #2

Этот пример схож с предыдущим, лишь отличается тем, что мы указываем открытую по умолчанию панельку. (см. пример)

В CSS у нас указано для всех элементов

display:none. Теперь нам необходимо открыть третью панель. Для этого мы можем написать следующий код:

dojo.addOnLoad(function(){
    // выбираем третий div и добавляем к заголовку класс active
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.accordion div:nth-child(3) h3&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;active&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);
    // работает лишь с webkit'ом
    // dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.accordion div:not(:nth-child(3)) p&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).style({ display:'none' });
    var els = dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.accordion div p&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);  // выбираем все параграфы
         els.splice(2,1); // вырезаем третий (тут отсчет с нуля идет)
         els.style({ display:'none' }); // все остальные скрываем
    /* ... */
});

Примечание: Селектор вида div:not(:nth-child(2)) заработал лишь в Safari и Chrome. Кстати, реализовал я анимацию в данном примере иначе – использовал dojo.fx.Toggler – достаточно забавная вещь, правда я так и не понял как правильно переключаться между show() и hide() (к примеру, если мне хочется создать функцию аля slideToggle в jQuery)

Анимация для события hover #1

Данный пример поможет создать Вам очень красивую анимацию для события hover (надеюсь, Вы знаете что это?), (см. пример):

Dojo Hover

Когда Вы наводите мышкой на элемент меню (mouseover), происходит поиск следующего элемента <em> и анимируются его прозрачность и расположение:

dojo.addOnLoad(function(){
    // вешаемся на событие onmouseover
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.menu a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onmouseover&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(){
        // ищем em элемент и анимируем его
        var em = dojo.query('em', this.parentNode);
        dojo.animateProperty({ node: em[0], duration:500,
            properties: {
                opacity: { start: 0, end: 1 },              // прозрачность
                top: { start:-85, end:-70, unit:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;px&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;  }  // располложение
            },
            beforeBegin:function() {  // перед началом анимации надо выставить правильно св-во display
                em.style({display:'block'});
            }
        }).play();
    });
    // вешаемся на событие onmouseout
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.menu a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onmouseout&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(){
        var em = dojo.query('em', this.parentNode);
        dojo.animateProperty({ node: em[0], duration:300,
            properties: {
                opacity: { start: 1, end: 0 },
                top: { start:-70, end: -85, unit:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;px&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;  }
            },
            onEnd:function() {
                em.style({display:'none'});
            }
        }).play();
    });

});

Анимация для события hover #2

Данный пример чуть-чуть посложней предыдущего примера: для формирования подсказки используется атрибут title (см. пример)

Dojo Hover 2

Первым делом добавим тэг <em> в каждый элемент <a>. Когда произойдет событие mouseover, мы возьмем текст из атрибута “title” и вставим его в тэг <em>:

dojo.addOnLoad(function(){
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.menu2 a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onmouseover&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(){
        // создаем елемент em и закидываем его в DOM
        var em = dojo.query(dojo.create('em', {innerHTML:dojo.attr(this, 'title')})).place(this.parentNode);
        // анимация
        dojo.animateProperty({ node: em[0], duration:500,
            properties: {
                opacity: { start: 0, end: 1 },
                top: { start:-85, end:-70, unit:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;px&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;  }
            },
            beforeBegin:function() {
                em.style({display:'block'});
            }
        }).play();
    });

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.menu2 a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onmouseout&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(){
        var em = dojo.query('em', this.parentNode);
        dojo.animateProperty({ node: em[0], duration:300,
            properties: {
                opacity: { start: 1, end: 0 },
                top: { start:-70, end: -85, unit:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;px&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;  }
            },
            onEnd:function() {
                // удаляем элемент из DOM'a
                em.orphan();
            }
        }).play();
    });

});

Кликабельные блоки

Этот пример демонстрирует, как сделать кликабельным блок с текстом, а не только ссылку (см. пример):

Создадим список <ul> с классом class=”pane-list” и мы хотим сделать элементы <li> кликабельными. Для начала привяжемся к событию click для элемента “.pane-list li”; когда пользователь будет кликать по элементу списка, наша функция произведет поиск тэга <a> и сделает редирект на страницу указанную в атрибуте href.

dojo.addOnLoad(function(){
     dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane-list li&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;click&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(){                           
         window.location=dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;, this).attr(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;href&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);return false;
     });
});

Складывающиеся панельки

Ну, а теперь чуть-чуть скомбинируем предыдущие примеры и создадим ряд складывающихся панелек (наподобие как в Gmail организован inbox). (см. пример)
 

  • скрываем все элементы <div class="message_body"> после первого.
  • скрываем все элементы <li> после пятого
  • клик по <p class=”message_head”> – вызывает метод wipeOut/wipeIn для следующего элемента <div class=”message_body”>
  • клик по <a class=”collpase_all_message”> – вызывает метод wipeOut для всех <div class=”message_body”>
  • клик по <a class=”show_all_message”> – скрывает элемент, и отображает <a class=”show_recent_only”>, так же вызывается метод wipeIn для всех <li> после пятого
  • клик по <a class=”show_recent_only”> – скрывает элемент, и отображает <a class=”show_all_message”>, так же вызывается метод wipeOut для всех <li> после пятого
dojo.require(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;dojo.fx&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);
dojo.require(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;dojo.NodeList-fx&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);
dojo.addOnLoad(function(){
// hide message_body after the first one
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_list li:not(:first-child) .message_body&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).style({ display:'none' });

// hide message li after the 5th
// dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_list li&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).splice(4).style({ display:'none' }); // not equal for FF and IE
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_list li:nth-child(1n+4)&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).style({ display:'none' });

// event on header click
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_head&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(){
    var body = dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_body&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,this.parentNode)
    if (dojo.style(body[0], 'display') != 'none') {
        body.wipeOut().play();
    } else {
        body.wipeIn().play();
    }
    return false;
});

// collapse all messages
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.collpase_all_message&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
    e.preventDefault();
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_body&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).wipeOut().play();
});

// show all messages
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.show_all_message&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
    e.preventDefault();
    dojo.query(this).fadeOut().play();
    dojo.style(this,'display','none');
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.show_recent_only&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).style('display','block').fadeIn().play();
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_list li:nth-child(1n+4)&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).wipeIn().play();
});

// hide old messages
dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.show_recent_only&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
    e.preventDefault();
    dojo.query(this).fadeOut().play();
    dojo.style(this,'display','none');
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.show_all_message&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).style('display','block').fadeIn().play();
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.message_list li:nth-child(1n+4)&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).wipeOut().play();
});
});

Примечание: заметил различное поведение функции dojo.NodeList.splice в различных браузерах

Имитация Backend’a WordPress’a

Я думаю многие из читателей сталкивались с админской частью wordpress’a, точнее с редактирование комментариев. Попробуем сделать что-то подобное (см. пример):

  • добавим класс “alt” к каждому чётному элементу <div class="pane"> (данный класс изменяет цвет фона элемента)
  • клик по <a class=”btn-delete”> инициирует появление сообщения (alert), так же происходит анимация фонового цвета и прозрачности (backgroundColor и opacity) для <div class=”pane”>
  • клик по <a class=”btn-unapprove”> – вызывает анимацию фона у <div class=”pane”> (цвет изменяется на желтый и обратно) и добавляет класс “spam”
  • клик по <a class=”btn-approve”> – вызывает анимацию фона у <div class=”pane”> (цвет изменяется на зеленый и обратно) и удаляет класс “spam”
  • клик по <a class=”btn-spam”> – вызывает анимацию фона у <div class=”pane”> (цвет изменяется на красный), затем вызываем FadeOut(), по завершению анимации элемент удаляем из DOM’а
dojo.require('dojo.fx');
dojo.addOnLoad(function(){
    // находим четные элементы
    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane:nth-child(even)&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;alt&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane .btn-delete&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
        // удаляем стандартный обработчик события - чтобы не было перехода по ссылке
        e.preventDefault();
        alert(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;This comment will be deleted!&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

        // выбираем необходимый нам блок
        var block = this.parentNode.parentNode;
        
        // вся анимация будет происходить последовательно
        var anim = dojo.fx.chain([
            dojo.animateProperty({ node: block, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#fbc7c7&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }}),
            dojo.fadeOut({ node: block})
        ]);

        // по завершению анимации блок будет спрятан
        dojo.connect(anim, &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onEnd&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;, function(){
            dojo.style(block, {'display':'none'});
        });

        anim.play();
    });

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane .btn-unapprove&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
        e.preventDefault();
        var block = this.parentNode.parentNode;

        dojo.fx.chain([
            dojo.animateProperty({ node: block, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#fff568&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }}),
            dojo.animateProperty({ node: block, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#ffffff&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }, onEnd:function() {
                    dojo.addClass(block, 'spam');
                }})

        ]).play();
    });

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane .btn-approve&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
        e.preventDefault();

        dojo.fx.chain([
            dojo.animateProperty({ node: this.parentNode.parentNode, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#dafda5&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }}),
            dojo.animateProperty({ node: this.parentNode.parentNode, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#ffffff&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }, onEnd:function() {
                    dojo.removeClass(this.node, 'spam');
                }})

        ]).play();
    });

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.pane .btn-spam&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect('onclick',function(e){
        e.preventDefault();

        dojo.fx.chain([
            dojo.animateProperty({ node: this.parentNode.parentNode, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#fbc7c7&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }}),
            dojo.fadeOut({ node: this.parentNode.parentNode, properties: { backgroundColor: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;#ffffff&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; }, onEnd:function() {
                    dojo.query(this.node).orphan();
                }})

        ]).play();
    });

});

Примечение: код каждой функции немного различается, так – для разнообразия

Галерея изображений

Простейший пример реализации галереи, без перезагрузки страницы. (см. пример)

Для начала добавим тэг <em> в заголовки <h2>
По клику на изображения в <p class=thumbs> выполняем следующие действия:

  • отменяем событие по умолчанию (это переход по ссылке)
  • сохраняем значение атрибута “href” в переменной “largePath”
  • сохраняем значение атрибута “title” в переменной “largeAlt”
  • заменяем в элементе <img id=”largeImg”> значение атрибута “scr” и “alt” значениями из переменных “largePath” и “largeAlt”
  • так же присваиваем элементу “h2 em” значение из “largeAlt”
dojo.addOnLoad(function(){
   dojo.query(dojo.create('em')).place(dojo.query('h2'));
   dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;.thumbs a&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).connect(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;onclick&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;,function(e){
        e.preventDefault();

        // получаем необходимые нам данные
        var largePath = dojo.attr(this, &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;href&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);
        var largeAlt  = dojo.attr(this, &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;title&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

        // заменяем картинку и alt-текст
        dojo.attr(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;largeImg&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;, {src:largePath,alt:largeAlt});
        em = dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;h2 em&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;)[0];

        // изменяем описание
        em.innerHTML = &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; (&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; + largeAlt + &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;)&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;;
    });
});

Стилизируем ссылки

Большинство нормальных браузеров легко понимают когда мы хотим добиться от них стилизации ссылок для различного типа файлов, для это цели можно использовать следующее CSS правило: a[href $='.pdf'] { ... }. Но как обычно IE6 отличается умом и сообразительностью, по этой причине будем ставить ему костыли используя Dojo. (см. пример

Для начала добавим класс для каждой ссылки, в соответствии с типом файла.
Затем выберем все элементы <a> которые не содержат ссылки на “http://anton.shevchuk.name” и не начинающиеся на “#” в “href”, затем добавим им класс "external" и устанавливаем target= "_blank".

dojo.addOnLoad(function(){

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;a[href$=pdf]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;pdf&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;a[href$=zip]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;zip&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;a[href$=psd]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;psd&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;);

    dojo.query(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;a:not([href*=http://anton.shevchuk.name])&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;).filter(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;:not([href^=#])&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;)
        .addClass(&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;external&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;)
        .attr({ target: &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;_blank&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; });

});

Так же Вы можете посмотреть все примеры или скачать Dojo для начинающих.

Полезные ссылки

Приведу ссылки на полезные ресурсы по теме:

© Антон Шевчук 2007-2023