Что плохо на примере jq.
Рассмотрим на задаче и её реализации: Есть x элементов, по клику на элемент он должен поменять состояние. Кликов на элементе может быть, и чаще всего будет, не одни.
Вёрстка.
Элементов много — нахождение по классу.
<body>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
<div class="testDiv"></div>
</body>
// бесспорно ок.
JS.
Найти по классу, повесить клик, обработать клик, отмечен — снять, не отмечен — отметить
jQ
подключили jQ
Решение Задачи
$(function(){
$('.testDiv').on('click', function(){
var vedroCache = $(this);
if (vedroCache.hasClass('isActiveTestDiv')){
vedroCache.removeClass('isActiveTestDiv');
} else{
vedroCache.addClass('isActiveTestDiv');
}
});
});
JS
Вместо jq
//писать ≈2—3ч в с чаем и твиттером в выходные
(function(u){
var J = {},
W = window,
D = document,
intCache;
W.J = J;
// search elements
if ('getElementsByClassName' in document){
J.$c = function(className, from){
return (from || D).getElementsByClassName(className);
}
} else{
J.$c = function(className, from){
var cache = (from || D).getElementsByTagName('*'),
result = [],
i = cache.length,
j = -1;
for (; i-- ;){
if (cache[i].className.indexOf(className) !== -1){
result[j += 1](cache[i]);
}
}
return result;
}
}
// events
if ('addEventListener' in W){
J.eventAdd = function(object, eventName, callback){
object.addEventListener(eventName, callback);
};
J.eventDel = function(object, eventName, callback){
object.removeEventListener(eventName, callback);
};
} else{
var ieFixEventsNameObjectsCallbacks = [],
ieFixEventsObjectsAndCallbacksLength = 0,
ieFixEventsHandlers = {},
fixEvent = function(e){
e.preventDefault = function(){
e.returnValue = false;
};
e.stopPropagation = function(){
e.cancelBubble = true;
};
e.target= e.srcElement;
return e;
},
ieAddFixEvent = function(object, eventName, callback){
function fix(){
callback.call(object, fixEvent(W.event));
}
intCache = ieFixEventsObjectsAndCallbacksLength;
ieFixEventsNameObjectsCallbacks[ieFixEventsObjectsAndCallbacksLength] = eventName;
ieFixEventsObjectsAndCallbacksLength += 1;
intCache += ieFixEventsObjectsAndCallbacksLength;
ieFixEventsNameObjectsCallbacks[ieFixEventsObjectsAndCallbacksLength] = callback;
ieFixEventsObjectsAndCallbacksLength += 1;
intCache += ieFixEventsObjectsAndCallbacksLength;
ieFixEventsNameObjectsCallbacks[ieFixEventsObjectsAndCallbacksLength] = object;
ieFixEventsObjectsAndCallbacksLength += 1;
ieFixEventsHandlers[intCache] = fix;
object.attachEvent('on' + eventName, fix);
},
ieRemoveFixEvent = function(object, eventName, callback){
for (var i = ieFixEventsObjectsAndCallbacksLength; i-- ;){
if ((ieFixEventsNameObjectsCallbacks[i] === object) && (ieFixEventsNameObjectsCallbacks[i - 1] === callback) && (ieFixEventsNameObjectsCallbacks[i - 2] === eventName)){
ieFixEventsNameObjectsCallbacks[i] = ieFixEventsNameObjectsCallbacks[i - 1] = ieFixEventsNameObjectsCallbacks[i - 2] = u;
i = i * 3 - 3;
break;
}
i -= 2;
}
if (i !== -1){
object.detachEvent('on' + eventName, ieFixEventsHandlers[i]);
ieFixEventsHandlers[i] = u;
}
};
J.eventAdd = function(object, eventName, callback, isNotNeedFix){
if (isNotNeedFix === true){
object.attachEvent('on' + eventName, callback);
} else{
ieAddFixEvent(object, eventName, callback);
}
};
J.eventDel = function(object, eventName, callback, isNotNeedFix){
if (isNotNeedFix === true){
object.detachEvent('on' + eventName, callback);
} else{
ieRemoveFixEvent(object, eventName, callback);
}
};
}
// classes
J.classHas = function(object, className){
return object.className.indexOf(className) !== -1; //да без регулярок*
};
J.classAdd = function(object, className){
if (!J.classHas(object, className)){
object.className += ' ' + className;
}
};
J.classDel = function(object, className){
if (J.classHas(object, className)){
object.className = object.className.replace(className, ''); //да без регулярок*
}
};
//*проектирование уникальных классов для DOM элементов вьюшек — простая стандартизируемая (автоматизируемая) задача!
// fast bad ready
J.ready = function(callback){
var callbacks = [callback];
function ready(){
var iMax = callbacks.length,
i = 0;
J.eventDel(D, 'DOMContentLoaded', ready);
J.eventDel(W, 'load', ready, true);
for (;i < iMax; i += 1){
callbacks[i].call(J);
}
ready = callbacks = null;
J.ready = function(callback){
callback.call(J);
}
}
if ('addEventListener' in W){
J.eventAdd(D, 'DOMContentLoaded', ready);
}
J.eventAdd(W, 'load', ready, true);
J.ready = function(callback){
callbacks.push(callback);
}
};
}());
Решение задачи
J.ready(function(){
var J = this,
elems = J.$c('testDiv'),
i = elems.length;
function clickListener(){
J[J.classHas(this, 'isActiveTestDiv') ? 'classDel' : 'classAdd'](this, 'isActiveTestDiv');
}
for (;i--;){
J.eventAdd(elems[i], 'click', clickListener);
}
});
До объяснений проблем ведра хотелось бы заметмть, инит решения при 350 дивах и следующей эмуляции console.time
if (!('console' in window) || !('time' in console)){
(function(w){
var times = {},
C;
if (!('console' in w)){
w.console = C = {};
C.log = function(data){
alert(data);
}
} else{
C = w.console;
}
C.time = function(name){
times[name] = new Date().getTime();
};
C.timeEnd = function(name){
if (name in times){
return new Date().getTime() - times[name];
}
};
}(window));
}
на машинке
и в фоксе 18.0.1
jQ ≈16ms
J ≈2ms
А в чём разница?
По мимо кучи кода, основная разница в подходах.
Подход «ведро»
Концепт — осознать, применить, т.е.
$(/*всё что угодно*/)./*сделай с ним что то*/
Мы можем менять jq на любые другие штуки, но концепт остаётся один и тот же.
Есть функция готовая ко всему (ведро), которая возвращает результат своей обработки в своей оболочке с набором действий.
В следствии чего
//мы должны надеятся на то, что ведро нас поймёт
$('.testDiv').on('click', function(){
var vedroCache = $(this); // и во многих элементарных случая мы должны обрабатывать через ведро каждый раз
if (vedroCache.hasClass('isActiveTestDiv')){
vedroCache.removeClass('isActiveTestDiv');
} else{
vedroCache.addClass('isActiveTestDiv');
}
});
console.log(console.timeEnd('divs'));
});
Подход «не ведро» (набор решений)
/*название решения*/(/*параметры задачи :)*/)
Считаю правильным, если решение валится от неправильных параметров.
J.ready(function(){
var J = this,
elems = J.$c('testDiv'), //найти по классу
i = elems.length;
function clickListener(){
J[J.classHas(this, 'isActiveTestDiv') ? 'classDel' : 'classAdd'](this, 'isActiveTestDiv'); //нет поставить, есть убрать
}
for (;i--;){
J.eventAdd(elems[i], 'click', clickListener); //повесить событие на конкретный элемент
}
});
Развивать тему по поводу контроля конкретных решений можно очень долго.
Вывод следующий: нужно максимально понимать и контролировать то что у вас происходит, не отдавая всё на откуп общепринятого ведра.
Автор: DmitryMakhnev