Delayed DOM Manipulations
Чебурашка решил начать разговор издалека. - Солнышко светит, травка зеленеет! - сказал он. - А нам вот так нужны гвозди! Не дадите немножко? - Это не травка зеленеет, - ответил кладовщик.- Это краску пролили. А гвоздей нет. Каждый ящик на учете.
Пожалуй эта цитата лучше всего характеризует то, что я сегодня вам приготовил. А подготовка материала как показала практика дело очень интимное. Я пару раз просил читателей сказать о чем они хотели бы почитать, но ни разу не написал, ни на одну из предложенных тем. Зато могу бросить все и начать писать о том, что мне пришло в голову как шальная мысль. Так и в этот раз, просматривая очередную заметку "100500 типс энд трикс для жуквери" я решил, что меня утомил один из этих типсов.
Самая простая вещь которая может быть это вынос за пределы цикла и отложенная работа с DOM деревом.
var lis = [1,2,3,4,5,6];
$(lis).each(function(i, e){
$("ul#my").append("<li>"+e+"</li>")
});
Превращаеться в
var lis = [1,2,3,4,5,6], html = "";
$(lis).each(function(i, e){
html = "<li>"+e+"</li>";
});
$("ul#my").html(html);
Кароче самая тривиальная и банальная вещь из существующих. Но писать append внутри цикла как-то логичнее и приятнее ну, по крайней мере, для меня. И решил я захачить жуквери так что бы можно было писать как хочеться а работало как надо. Вот тут собственно и уместно предисловие, пишем append внутри each и каждая миллисекунда на счету.
После дня ковыряния у меня родился вот такой код:
(function($){
/*
* @author CTAPbIu_MABP
* @email CTAPbIuMABP@gmail.com
* @link http://mabp.kiev.ua/2010/08/31/delayed-dom-manipulations/
* @license GPL
*/
var data = {}, // тут список всех элементов и отложенных операций над ними
isOuterLoop = true, // определяет самый внешний вложенный цикл
supportedFunctions = [ // список переопределяемых методов
"append",
"prepend",
"before",
"after"
];
/**
* Складывает аргументы оригинальных методов в data
* @param funcName {String} имя оригинального метода
* @param args {array-like object} аргументы оригинального метода
* @return {jQuery} возвращяет jQuery для продолжения цепочки
*/
function delay (funcName, args){
var i = $.inArray(this, data[funcName].elt),
args = $(args).toArray(); // приводим аргументы к массиву
if (i > -1){
data[funcName].tmp[i] = data[funcName].tmp[i].concat(args);
}else{
data[funcName].elt.push(this);
data[funcName].tmp.push(args)
}
return this;
}
// создаем место для хранения данных
// и обертки ко всем поддерживаемым методам
$(supportedFunctions).each(function(i, funcName){
data[funcName] = {tmp : [], elt : []};
$.fn[funcName+"Delayed"] = function(){
return delay.call(this, funcName, arguments)
}
});
// создаем обертку для each
$.fn.eachDelayed = function(){
var ret, isOuter = isOuterLoop;
isOuterLoop = false;
ret = $.fn.each.apply(this, arguments); // выполняем оригинальный each
if (isOuter){ // после самого вернего вложенного цикла
$.each(data, function(funcName, funcParam){ // обходим все данные
$(funcParam.elt).each(function(i){ // и выполняем все оригинальные методы
$.fn[funcName].apply(funcParam.elt[i], funcParam.tmp[i]);
});
data[funcName] = {tmp : [], elt : []}; // обнуляем данные
})
isOuterLoop = true;
}
return ret;
}
})(jQuery);
Ну что ж погнали, посмотрим что он делает. А делает он обертку для методов из массива supportedFunctions, грубо говоря AOP. На самом деле переопределенные методы не выполняют вставку в DOM дерево, а только вызывают функцию delayed которая складывает аргументы оригинальных методов в массив data. После конца цикла все накопленные данные проверяются и выполняются все отложенные методы.
Насколько я проверил это все прекрасно работает с несколькими вставками одновременно, во вложенных циклах и несколькими аргументами, а так же со всем этим вместе.
Теперь я думаю надо объяснить на чем основан хак. Так сложилось что все четыре метода из массива supportedFunctions используют метод domManip (line 4263) -> buildFragment (line 4355) -> clean (line 4417) передавая все свои аргументы, для построения DocumentFragment одним махом. Вот этим-то я и воспользовался, в самом внешнем цикле я один раз вызываю оригинальный метод с кучей параметров.
Еще два слова о том как пользоваться этим чудом
$(document).ready(function(){
var lis = [1,2,3,4,5,6],
ul1 = $("ul#my1"),
ul2 = $("ul#my2");
$(lis).eachDelayed(function(i, e){
ul1.beforeDelayed("<div>"+e+"</div>");
ul1.appendDelayed("<li>"+e+"</li>");
ul2.prependDelayed("<li>1+"+e+"</li>", "<li>e+"+e+"</li>");
$(lis).eachDelayed(function(i, e){
ul2.appendDelayed("<li>2+"+e+"</li>");
});
});
});
Результат выполнения кода приводить не буду, там ничего красивого, зато работает. Все методы before, append и prepend выполняются по одному разу при окончании внешнего цикла.
Хотел еще сделать поддержку для всяких wrap, но что-то там как-то грустно все, они не используют domManip.
Скачать можно тут DOWNLOAD LINK
Свежие комментарии