В един от последните ми проекти ми се налагаше да генерирам много html елементи с javascript. Като за това основно използвах new Element() и Element#insert от Prototype. Като въпреки че като цяло двата метода са много изчистени и правят кода доста добре четим, винаги може и по-добре.
В началото написах един прост builder, но като малко го поизползвах тук и там, се оказа , че като код е доста по-малко, но не и като четимост. Затова написах и тези две малки добавки към Element#insert които ми спестиха доста писане и направиха кода още по-четим и добър. И същевременно показаха (за пореден път) няколко от добрите страни на Prototype.
Element.insert(element, { into: content });
Element.insert(element, [content, content, content]);
Element#insert(element, { into: content });
Тук просто добавих нов insert position, into. Общо взето е ясно какво прави той добавя елемента в друг елемент. Защото поне на 20 места имах нещо такова(или много подобно:
var element = new Element('div', {/* атрибут */});
document.body.appendChild(element);
// като с into това просто става
var element = new Element('div', {/* атрибут */}).insert({into: document.body });
За тези, които са запознати как работи точно от вътре Element#insert, няма да е никак трудно да разберат как съм добавил ‘into’. Prototype използва един object, който да съхранява всички insert функции – before, after, top, bottom. Той е
Element._insertionTranslations, и когато бива извикан Element#insert(element, {position: content}) се вика грубо казано Element._insertionTranslations[position](element, content). Заради това добавянето на нов insert метод е много лесно и става просто така:
Element._insertionTranslations.into = function(element, node){
node.appendChild(element);
};
Внимание! Element._insertionTranslations не е част от Public Api-то на Prototype и затова може да се промени в бъдещи версии!
Страничен ефект
Element#insert(element, { into: content }) има много интересен ефект, за който не се бях сетил когато го писах. Но когато случайно “открих” бях много доволен.
Element#insert може да приема не само html елементи, но и нормални javascript обекти, които имат метод toElement ( тои трябва да връща html елемент). И така примерно имам клас подобен на Custom Select класа ми от ControlDepo 3 Widgets, който има един контейнер, към когото може да се добавят елементи. Чрез into могат да се правят такива неща:
var list = new (Class.create({
initialize: function(ul){
this.element = $(ul);
},
toElement: function(){
return this.element;
}
}))('some');
Тук, за декларацията на класа, ползвам прост номер описан, по-добре тук.
Element.insert(element, [content, content, content]);
Идеята тук е да може да се предава масив от елементи, които да се добавят в element.
var ul = new Element('ul');
ul.insert(li1);
ul.insert(li2);
ul.insert(li3);
// ... и т.н.
// като това понякога го пиша и така
[li1, li2, li3].each(Element.insert.curry(ul));
// с новата версия става просто
ul.insert([li1, li2, li3]);
Такава функционалност може да стане по 2 начина. Първо като пиша директно в кода на Element#insert ( или си направя мой си fork на Prototype). И втория вариянт е Function#wrap.
Колкото и да се изкушавам от първия вариант реших само да пусна един билен ( ticket ) на Prototype и да се надявам да го добавят като функционалност. И след това си написах каквото ми трябваше чрез Function#wrap:
Element.addMethods({
insert: Element.insert.wrap(function(insert, element, insertation){
if (!Object.isArray(insertation)) return insert(element, insertation);
element = $(element);
insertation.each(insert.curry(element));
return element;
})
});
Тези двете добавки ги има в git.github – http://gist.github.com/164751. Там ще ги актуализирам ако правя промяна.
Ако някой има предложение или идея как може да се доразвият тези добавки или за други подобни, може да драсне някои коментар
