суббота, 16 января 2010 г.

Исследуем новые возможности jQuery 1.4

14 января был 4-й день рождения jQuery. К этому дню и был приурочен выпуск релиза jQuery 1.4. Цельного понимания изменений не было, информация была немного скупа, вот я и решил попробовать его в действии, вследствие чего и родилась данная статья.

Так как версию jQuery 1.3.2 я использовал уже довольно долго, то хотелось найти ответ на следующий вопрос: "Стоит ли переходить и что придется менять в старом коде для перехода на jQuery 1.4?".

Совместимость с другими версиями jQuery


Разработчики, которые раньше использовали версию jQuery 1.3.2, могут смело переходить на использование версии jQuery 1.4, они полностью совместимы (одна оговорка, см. jQuery.param). Во всех остальных случаях вам придется проверить совместимость вашего кода с jQuery 1.4.

Данная версия обладает рядом преимуществ, одним из которых является оптимизация и повышения быстродействия методов addClass, removeClass, append, html и других.

Производительность методов(источник)

СSS


Как видно из графика, была проведена работа по оптимизации методов взаимодействующих с css. Для удобства был расширен функционал методов .css(), .addClass(), .removeClass(), .toggleClass(), .offset().

- .css(propertyName,function(index,value)).
Метод css необходим для определения и установки css свойств. Теперь определять значение css свойства можно в динамике.

Внимание: В документации по jQuery 1.4 в описании данного метода допущена ошибка. Данный метод принимает объект map, где значение может быть определено функцией function(index,value).
$(this).css({
  width : function(index, value) {
        //index - индекс текущего элемента
        //value - старое значение этого свойства
        return parseFloat(value) * 1.2;
      },
  height: "20px" 
});
- .addClass(function(index,class)), .removeClass(function(index,class)), .toggleClass(function(index,class),[switch])
Группа данных методов необходима для добавления/удаления css классов. Теперь методы могут в качестве значений принимать функцию, которая должна вернуть строку с значением класса. Если нужно передать несколько классов, то они должны быть разделены пробелом.
$("#myID").addClass(function(index, className) {
 //index - индекс текущего элемента
 //className - старое значение class
 return "myclass";
});

$("#myID").removeClass(function(index, className){
 if (index==0) {
    return "myclass";
 } else {
    return "";
 }
});
- .offset(coordinates), .offset(function(index,coords))
Метод .offset() в предыдущих версиях jQuery возвращал объект, который описывал положение DOM элемента относительно объекта document. В текущей версии функционал данного метода был расширен, и теперь можно не только узнать положение, но и установить его.
//Смещаем элемент на 20 пикселей по оси X, и на 10 по оси Y
$("#myID").offset(function(index, coordinate){
  //index - индекс текущего элемента
  //coordinate - текущие координаты смещение элемента
  return { top: coordinate.top+20, left: coordinate.left+10 };
});

//Перемещаем элемент в левый верхний угол экрана
$("#myID").offset({ top: 0, left: 0 });
Внимание: При исследовании заметил маленькую особенность в работе метода offset принимающего функцию.
1. Если изменить значение coordinate.top или/и coordinate.left, а в качестве значения вернуть объект coordinate, то смещение НЕ произойдет.
2. Если изменить значение coordinate.top или/и coordinate.left, а в качестве значения вернуть новый объект с необходимыми координатами, то при смещении элемент НЕ будет находится в задаваемой точке.


Детальная информация: css, addClass, removeClass, toggleClass, offset.




Создание/добавление DOM элементов


Была проведена существенная работа и по доработке методов создания/добавления DOM элементов.

- cоздание DOM элементов jQuery(html,props)
В своей статье "jQuery tips. Часть 1. 11 моментов о которых необходимо знать" я описывал возможность jQuery по созданию html элементов. В версии 1.4 появилась возможность добавлять атрибуты при создании элемента, передавая их вторым параметром:

jQuery('<a/>', {  
  id: 'jquery14',  
  href: 'http://jquery14.com/day-01/jquery-14',  
  title: 'jQuery 1.4',  
  rel: 'ext_link',  
  text: 'Release jQuery 1.4'  
});
Как вы могли уже заметить тег "a" не имеет атрибута text. В jQuery 1.4 была добавлена возможность устанавливать не только атрибуты, но и вызывать методы jQuery. Так, в данном случае будет вызван метод ".text()" и в качестве параметра будет передано значение "Release jQuery 1.4"

Вот пример который продемонстрирует всю мощь таких атрибутов в jQuery 1.4

jQuery('<div/>', {
  id: 'jquery',  
  css: {
         fontWeight: 500,
         color: 'red'
  },
  click: function(){
         alert('Используем jQuery 1.4!');
  }
});

Кроме присвоения значения атрибуту id, в данном примере устанавливается значение css свойств и добавляется обработчик событий click.

- .after(function(index)), .before(function(index))
Метод after/before добавляет передаваемый в параметре контент после/до каждого элемента в выборке. Теперь данные методы могут принимать функцию, которая должна вернуть Html строку или DOM элемент.

$("#myID").after(function(index){
  //index - индекс текущего элемента
  return document.createTextNode("Используем jQuery 1.4.");
});

$("#myID").before(function(index){
  return "<div>jQuery уже подрос, ему 4 года :).</div>";
});
- .append(function(index,html)), .prepend(function(index,html))
Метод append/prepend добавляет передаваемый в параметре контент в конец/начало каждого элемента в выборке. И в эти методы также можно передавать первым параметром функцию, она должна вернуть Html строку или DOM элемент.
$("#myID").append(function(index, html){
  //index - индекс текущего элемента
  //html - innerText найденного элемента
  return "А вы используете jQuery 1.4?";
});
- .html(function(index,html)), .text(function(index,text)), .val(function)
Возможности этих методов были расширены, и теперь каждая может принимать функцию.
$("#myID").text(function(index, text){
  //index - индекс текущего элемента
  //text - старое значение текста
  return "А вы участвуете в акции '14 Days of jQuery, 15 Days of prizes.'?";
});

$("#myID").html(function(index, html){
  //index - индекс текущего элемента
  //html - старое значение html
  return "<a href='http://mediatemple.net/jquery14/'>14 Days of jQuery, 15 Days of prizes.</a>";
});

$("#myID").val(function(index, value){
  //index - индекс текущего элемента
  //value - старое значение value
  return '13” Apple MacBook Pro хороший приз для конкурса :)';
});

Детальная информация: jQuery, after, before, append, prepend, html, text, val.




Работа с DOM элементами


В новой версии также добавлены некоторые интересные методы по работе с DOM элементами.

- .wrap(wrappingFunction), .wrapInner(wrappingFunction), .wrapAll(wrappingFunction)
Эти методы для каждого элемента в выборке создают дополнительную обертку. Они были доступны с версии 1.2, но в версии 1.4 эти методы могут принимать функцию. Функция должна вернуть структуру, которая будет использована в качестве обертки.
$("#myID").wrap(function(index){
  //index - индекс текущего элемента
  return "<div></div>";
});

$("#myID").wrapInner(function(index){
  //index - индекс текущего элемента
  return document.createElement("b");
});
- .unwrap()
Также был добавлен метод ".unwrap()", который осуществляет противоположное действие методу ".wrap()", т.е. удаляет родительский элемент(обертку) у всех элементов в выборке.
$("#myID").unwrap();
- .detach()
Данный метод удаляет элемент из DOM. Действует подобно методу .remove(), но сохраняет все ассоциации jQuery с этим объектом, т.е. все данные добавлены к этому элементу методом .data() и все обработчики событий добавленные через jQuery будут сохранены.

Этот метод будет полезен в том случае, если вам необходимо удалить элемент, но вы знаете, что через время его все равно придется создавать.
myEl = $("#myID").detach(); //удаляем объект из DOM
....
myEl.appendTo("body"); //добавляем объект
- .toArray()
Очень простой метод, возвращает список элементов, которые были выбраны селектором.
Html:
<ul>
    <li>jQuery 1.2</li>
    <li>jQuery 1.3</li>
    <li>jQuery 1.4</li>
    <li>more</li>
</ul>

JavaScript:
var list = $("li").toArray();
//list = [li, li, li, li];
- .replaceWith(function)
Данный метод заменит каждый выбранный элемент значением, которое будет возвращено функцией. Функция может возвращать Html код или DOM элемент.
$("#myId").replaceWith(function(index){
  //index - индекс текущего элемента
  return "<div>Use replaceWith method in jQuery 1.4!</div>";
});
- .index(), .index(selector)
Метод ".index(element)" доступен с версии 1.0, но теперь у него появилось еще несколько вариантов использования.
var listItem = $('#myID');
alert('Index: ' + $('li').index(listItem));

$('li').click(function(){
  alert('Index: ' + '$(this).index());
})

Детальная информация: wrap, wrapAll, wrapInner, detach, toArray, replaceWith, index.




Сохранение области видимости


Думаю, одним из самых интересных и в тоже время простых методов, которые были добавлены в jQuery 1.4, является jQuery.proxy(). Данный метод имеет следующий синтакс jQuery.proxy(function,scope), jQuery.proxy(scope,name).

Продемонстрирую на примере, описанном в документации jQuery 1.4
Html:
<button id="test" name="test">Test button</button>
JavaScript:
var obj = {
  name: "John",
  test: function() {
    alert(this.name);
  }
};

Если выполнить функцию "obj.test()", то при выполнении, область действия функции будет объект obj, т.е. переменная this будет иметь значение obj, и this.name будет равняться "John".

Добавим в качестве обработчика клика данную функцию
JavaScript:
$("#test").click(obj.test);

В этом случае при выполнении область действия функции будет кнопка button, т.е. переменная this будет иметь значение "[button id="test"]", и this.name будет равняться "test".

Для того чтобы можно было сохранить области действия функции, был добавлен метод jQuery.proxy(). Работа данного метода заключается в том, что он вместо непосредственного обработчика возвращает "proxy" функцию, которая при выполнении вызовет необходимую(в нашем случае obj.test) с переопределением области вызова(this) на необходимый(в нашем случае obj).

JavaScript:
$("#test").click(jQuery.proxy(obj,"test"));
Вот таким простым способом можно сохранить область действия.
Детальная информация: jQuery.proxy




Методы проверки вхождения


- .has()
Добавлена новый метод .has(), который проверяет вхождения. Действует он аналогично селектору ":has()". В качестве аргумента проверки метод может принимать jQuery селектор или DOM элемент.
jQuery('div').has('ul');
//аналогично
jQuery('div:has(ul)');

- jQuery.contains()
Это новый метод в версии 1.4, который проверяет вхождения одного DOM элемента в другой. Использовать его чрезвычайно просто.
jQuery.contains(document.documentElement, document.body); // true
jQuery.contains(document.body, document.documentElement); // false

Детальная информация: has, contains.




Выбор элементов "До"


В тяжелый арсенал селекторов былы добавлены 3 метода: "nextUntil", "prevUntil", "parentsUntil".

Методы "nextUntil", "prevUntil" предназначены для ограничения выбора элементов. Выбор элементов будет прекращен, когда условие, переданное в качестве первого аргумента, не будет истина. Например, если у вас есть следующий html
Html:
<ul>
    <li>jQuery 1.2</li>
    <li>jQuery 1.3</li>
    <li>jQuery 1.4</li>
    <li>more</li>
</ul>

JavaScript:
jQuery('ul li:contains(jQuery 1.2)').nextUntil(':contains(more)');
// будут выбраны значения jQuery 1.3, jQuery 1.4
Метод "parentsUntil" действует похожим способом с двумя предыдущими аналогами, но выбирает все родительские элементы пока условие селектора ложно.

Детальная информация: nextUntil, prevUntil, parentsUntil.




Множественное добавление обработчиков событий


В статье "jQuery tips. Часть 1. 11 моментов о которых необходимо знать" я уже писал об интересном решении от Keegan Watkins, которое позволяло добавлять множество обработчиков событий в одном методе.

Как видим, разработчики jQuery добавили такую возможность в jQuery 1.4.
var i = 0;
//Добавляем обработчики событий
$("#myID").bind({
 click : function(event) {
  //обработчик события click
  i += 1;
  if (i>=100) {
     $this.unbind(event);
  }
 },
 mouseover : mouseoverHandler,
 mouseout : mouseoutHandler
});

function mouseoverHandler(e) {
 //обработчик события mouseover
}

function mouseoutHandler(e) {
 //обработчик события mouseout
}

$("#myID").unbind("click"); //Удаляем все обработчики события click
$("#myID").unbind(); //Удаляем ВСЕ обработчики событий
Детальная информация: bind, unbind.




Новые события в jQuery


В jQuery 1.4 можно выделить 2 интересных связанных с событиями новшества, были добавлены новые события ".focusin()", ".focusout()" и появилась возможность делегировать дополнительные типы событий используя метод ".live()".

- ".focusin(handler(eventObject))" возникает, когда элемент или дочерние элементы получают фокус, ".focusout(handler(eventObject))", когда элемент или дочерние элементы теряют фокус.

Добавление этих событий меня немного удивило. Чем не устроили события "focus", "blur"?

Объяснение было найдено при прослушивании jQuery Podcast with John Resig. Суть заключается в том, что события "focus", "blur" по спецификации W3C Document Object Model (DOM) Level 2 Events Specification не имеют возможности "всплывания"(bubbles) событий(крайне важный момент при использовании в делегировании событий :) ). Во всех современных браузерах, кроме IE, данный момент был реализован в точности со спецификацией.

Но в тоже врямя, в спецификации W3C описаны событий DOMFocusIn, DOMFocusOut, которые как раз имеют указанную возможность. Команда разработчиков приняла решение упростить названия и добавить еще 2 новых события. Возможность всплывания и реализация данных событий позволила расширить возможности их делегирования, используя метод ".live()".
$("#myID").focusin(function(){
  //элемент получил фокус
});

$("#myID").focusout(function() {
  //элемент потерял фокус
});
- .live()
Расширен список событий, которые можно делегировать с помощью данного метода. Добавлена поддержка "submit", "change", "focusin" и "focusout" событий. Также появилась расширенная аннотация ".live(eventType,[eventData],handler)".
jQuery('input').live('focusin', function(){
    // фокус передан элементу
});
Детальная информация: .focusin(), .focusout(), .live()




Расширенные возможности анимации


В данную версию jQuery был внесен ряд интересных функций для анимации.

- Удаление событий с очереди
Появился новый метод ".clearQueue([queueName])". При вызове метода без параметров все события, которые были добавлены в очередь с использованием метода ".queue()" и не были запущены, удаляются.

Параметр queueName необходим для удаления событий в определенной очереди, по умолчанию - fx. Данный метод выполняет те же действия, что и вызов метода ".stop(true)", но в отличии от метода ".stop()" может применяться не только в анимации.

- Пауза при анимации
До версии 1.4 не было реализовано возможности паузы в анимации. Для реализации необходимо было использовать javascript функцию "setTimeout". На базе плагина jQuery Delay от Clint Helfers был добавлен функционал реализующий задержку. Необходимо вызвать метод "delay()", передав первым параметром количество миллисекунд задержки.
jQuery('#myId')
    .fadeOut()
    .delay(300) // Задержка на 300 мс
    .fadeIn();
Детальная информация: .clearQueue(), .delay()




Методы по работе с объектами


- jQuery.isEmptyObject(object)
Данный метод проверяет пустой ли объект. Вернет значение false в случае, когда объект будет иметь хотя бы одно свойство.
jQuery.isEmptyObject({}); //true
jQuery.isEmptyObject(null); //true
jQuery.isEmptyObject({ item: null }); //false
- jQuery.isPlainObject(object)
Этот метод проверяет, является ли объект плоским, т.е. он должен иметь тип "Object", не должен иметь своих конструкторов и не может иметь свойств от других объектов. На практике такие объекты могут быть созданы 2-мя путями:
var plainObject1 = {};
var plainObject2 = new Object();
jQuery.isPlainObject({}); // true
jQuery.isPlainObject(new Object()); // true
jQuery.isPlainObject({ release: "jQuery 1.4" }); // true
jQuery.isPlainObject(document.createTextNode("Используем jQuery 1.4.")); //false
- сериализация объектов jQuery.param
В jQuery 1.4 добавлена поддержка вложенных сериализаций параметров с использованием метода jQuery.param, который пользуется популярностью в PHP и поддерживается в Ruby on Rails.

При сериализации объекта "{foo: ["bar", "baz"]}" мы получим следующий результат:
jQuery.param({foo: ["bar", "baz"]});
//jQuery 1.4 - "foo[]=bar&foo[]=baz"
//jQuery 1.3.2 - "foo=bar&foo=baz"
При переходе на jQuery 1.4 данное изменение может нарушить работу вашего кода. Если вы хотите использовать старый способ, то вам необходимо будет воспользоваться следующим решением:
// Включаем старый способ сериализации на уровне всего фреймворка
jQuery.ajaxSettings.traditional = true;
 
// Включаем старый способ сериализации для одной операции
jQuery.param( stuff, true );
 
// Включаем старый способ сериализации для одного Ajax запроса
$.ajax({ data: stuff, traditional: true });

Детальная информация: jQuery.isPlainObject, jQuery.isEmptyObject, jQuery.param



Еще несколько бесполезных функций:
jQuery() - возвращает пустой список.
jQuery.noop() - возвращает пустую функцию.

Детальную информацию вы можете найти на сайте jQuery, 14 Days of jQuery, jQuery 1.4 API Cheat Sheet.

4 комментария:

  1. Момойму самая лучшая дока по 1.4 получилась!)
    Так держать!)

    Кстати, жалко, что пока ещё нельзя так:
    $( 'тег a',{
    id:"jof",
    text: [
    'тег strong',
    {
    text:'sd'
    }
    ]
    });

    ОтветитьУдалить
  2. Не понял что именно тебе нужно, но возможно поможет следующий пример.

    var el = $('<a/>', {
    id:"jof",
    html: $("<strong/>", { text: "fd" } )
    });
    $("body").append(el);

    ОтветитьУдалить
  3. Да, нехилый у вас обзорчик! Кстати, вчера jQuery 1.4.1 вышла

    ОтветитьУдалить
  4. Ну в jQuery 1.4.1 они только навели марафет в api и поправили баги. Детально можно прочитать вот тут jQuery 1.4.1 Released

    ОтветитьУдалить