Я хотел написать какой-то чистый javascript, чтобы лучше понять его (в «реальной практике» я понимаю, что фреймворки, такие как jQuery, гораздо более советуемы и применимы, но на самом деле речь идет не о том, как использовать фреймворки, а о том, как чисто javascript работает и лучшие практики).
В любом случае, я написал простой код javascript. Я хотел создать набор групп кнопок, которые имели бы одно состояние за один раз из набора {вкл, выкл}, и каждое состояние соответствовало бы соответствующей функции, которая будет запускаться при входе в это состояние. Каждая группа кнопок в основном наборе может одновременно содержать только одну кнопку во включенном состоянии. Концепция похожа на идею радио кнопок. Почему бы тогда не использовать радио-кнопку? Ну, семантически, это просто предположить, что это несколько кнопок для некоторых элементов управления, но в любом случае, я полагаю, что это возможно, но вопрос не в этом.
Суть в том, что для этого я добавил множество пользовательских атрибутов к конкретным button
элементам id
в моем Javascript. Я провел некоторое исследование и нашел этот вопрос и вопрос , касающиеся использования пользовательских атрибутов на узле DOM (объектах). Похоже, они выступают против такой практики, можно даже сказать, что это может привести к потенциальной утечке памяти в зависимости от реализации браузера.
Однако для каждой создаваемой мной кнопки мне нужно отслеживать множество атрибутов, и если я расширю этот скрипт, мне, возможно, придется добавить еще больше. Так как лучше всего хранить их на узле DOM, но при этом отслеживать все и иметь возможность использовать this в прикрепленных функциях и т. Д. Al?
Для меня не было очевидным, как это сделать без минимального сохранения ссылки на объект, расположенный на расстоянии от имени скважины, до элемента DOM node button
.
Мне удалось увидеть, что из этого вопроса jQuery есть какой-то способ сделать это, но я хочу знать, как это сделать только с чистым javascript.
Вот полный пример кода, с которым я работаю:
<!DOCTYPE html>
<html>
<head>
<title>Button Test Script</title>
<script language="javascript" type="text/javascript">
window.button_groups = {};
function isset( type ) {
return !(type==='undefined');
}
function debug( txt ) {
if( !isset(typeof console) ) {
alert( txt );
} else {
console.log(txt);
}
}
function img( src ) {
var t = new Image();
t.src = src;
return t;
}
function turnGroupOff( group ) {
if( isset(typeof window.button_groups[group]) ) {
for( var i = 0; i < window.button_groups[group].length; i++ ) {
if( window.button_groups[group][i].toggle == 1)
window.button_groups[group][i].click();
}
}
}
/**
* buttonId = id attribute of <button>
* offImg = src of img for off state of button
* onImg = src of img for on state of button
* on = function to be fired when button enters on state
* off = function to be fired when button enters off state
*/
function newButton( buttonId, offImg, onImg, group, on, off ) {
var b = document.getElementById(buttonId);
b.offImg = img( offImg );
b.onImg = img( onImg );
b.on = on;
b.off = off;
b.img = document.createElement('img');
b.appendChild(b.img);
b.img.src = b.offImg.src;
b.group = group;
b.toggle = 0;
b.onclick = function() {
switch(this.toggle) {
case 0:
turnGroupOff( this.group );
this.on();
this.toggle = 1;
this.img.src = this.onImg.src;
break;
case 1:
this.off();
this.toggle = 0;
this.img.src = this.offImg.src;
break;
}
}
if( !isset(typeof window.button_groups[group]) )
window.button_groups[group] = [];
window.button_groups[group].push(b);
}
function init() {
var on = function() { debug(this.id + " turned on") };
newButton( 'button1', 'images/apply-off.jpg', 'images/apply-on.jpg', 'group1',
on,
function() { debug(this.id + " turned off"); }
);
newButton( 'button2', 'images/unapply-off.jpg', 'images/unapply-on.jpg', 'group1',
on,
function() { debug(this.id + " turned off (diff then usual turn off)"); }
);
newButton( 'button3', 'images/apply-off.jpg', 'images/apply-on.jpg', 'group2',
on,
function() { debug(this.id + " turned off (diff then usual turn off2)"); }
);
newButton( 'button4', 'images/unapply-off.jpg', 'images/unapply-on.jpg', 'group2',
on,
function() { debug(this.id + " turned off (diff then usual turn off3)"); }
);
}
window.onload = init;
</script>
</head>
<body>
<button id="button1" type="button"></button>
<button id="button2" type="button"></button>
<br/>
<button id="button3" type="button"></button>
<button id="button4" type="button"></button>
</body>
</html>
UPDATE
JQuery был немного излишним для моих целей. Мне не нужно расширять произвольный элемент. У меня есть хорошее представление о том, как это делается сейчас для jQuery (с произвольным атрибутом со случайным именем, хранящим целое число индекса кэша).
Я заранее знаю, какие элементы хоста мне нужно расширить и как; также я могу / хочу установить атрибут id
для каждого из них на стороне HTML.
Итак, вдохновленный настройкой jQuery, я решил также создать глобальную переменную кэша, за исключением того, что я собираюсь использовать атрибут id узла DOM в качестве ключа кэша. Поскольку это должен быть уникальный идентификатор (по определению), и у меня нет планов динамически изменять id, это должно быть простой задачей. Он полностью отделяет мои объекты Javascript от объектов DOM, но делает мой код немного уродливым и трудным для чтения с многочисленными вызовами data
. Я представляю изменения ниже:
<!DOCTYPE html>
<html>
<head>
<title>Button Test Script</title>
<script language="javascript" type="text/javascript">
window.button_groups = {};
function isset( type ) { // For browsers that throw errors for !object syntax
return !(type==='undefined');
}
var c = { // For browsers without console support
log: function( o ) {
if( isset(typeof console) ) {
console.log(o);
} else {
alert( o );
}
},
dir: function( o ) {
if( isset(typeof console) ) {
console.dir(o);
}
}
};
function img( src ) { // To avoid repeats of setting new Image src
var t = new Image();
t.src = src;
return t;
}
var cache = {};
function data( elemId, key, data ) { // retrieve/set data tied to element id
if(isset(typeof data)) {// setting data
if(!isset(typeof cache[elemId]))
cache[elemId] = {};
cache[elemId][key] = data;
} else { // retreiving data
return cache[elemId][key];
}
}
var button_groups = {}; // set of groups of buttons
function turnGroupOff( group ) { // turn off all buttons within a group
if( isset(typeof window.button_groups[group]) ) {
for( var i = 0; i < window.button_groups[group].length; i++ ) {
if( data(window.button_groups[group][i].id, 'toggle') == 1)
window.button_groups[group][i].click();
}
}
}
/**
* buttonId = id attribute of <button>
* offImg = src of img for off state of button
* onImg = src of img for on state of button
* on = function to be fired when button enters on state
* off = function to be fired when button enters off state
*/
function newButton( buttonId, offImg, onImg, group, on, off ) {
var b = document.getElementById(buttonId);
data( b.id, 'offImg', img( offImg ) );
data( b.id, 'onImg', img( onImg ) );
data( b.id, 'on', on );
data( b.id, 'off', off );
var btnImg = document.createElement('img');
btnImg.src = data( b.id, 'offImg' ).src;
data( b.id, 'img', btnImg );
b.appendChild( btnImg );
data( b.id, 'group', group );
data( b.id, 'toggle', 0 );
var click = function() {
switch(data(this.id,'toggle')) {
case 0:
turnGroupOff( data(this.id,'group') );
(data(this.id,'on'))();
data(this.id,'toggle',1);
data(this.id,'img').src = data(this.id,'onImg').src;
break;
case 1:
(data(this.id,'off'))();
data(this.id,'toggle',0);
data(this.id,'img').src = data(this.id,'offImg').src;
break;
}
}
b.onclick = click;
if( !isset(typeof window.button_groups[group]) )
window.button_groups[group] = [];
window.button_groups[group].push(b);
}
function init() {
var on = function() { c.log(this.id + " turned on") };
newButton( 'button1', 'images/apply-off.jpg', 'images/apply-on.jpg', 'group1',
on,
function() { c.log(this.id + " turned off"); }
);
newButton( 'button2', 'images/unapply-off.jpg', 'images/unapply-on.jpg', 'group1',
on,
function() { c.log(this.id + " turned off (diff then usual turn off)"); }
);
newButton( 'button3', 'images/apply-off.jpg', 'images/apply-on.jpg', 'group2',
on,
function() { c.log(this.id + " turned off (diff then usual turn off2)"); }
);
newButton( 'button4', 'images/unapply-off.jpg', 'images/unapply-on.jpg', 'group2',
on,
function() { c.log(this.id + " turned off (diff then usual turn off3)"); }
);
}
window.onload = init;
</script>
</head>
<body>
<button id="button1" type="button"></button>
<button id="button2" type="button"></button>
<br/>
<button id="button3" type="button"></button>
<button id="button4" type="button"></button>
</body>
</html>
ОБНОВЛЕНИЕ 2
Я обнаружил, что используя силу замыкания Мне действительно нужно хранить только один «специальный» атрибут, то есть группу, к которой принадлежала кнопка.
Я изменил функцию newButton
на следующую, что благодаря закрытию избавляет от необходимости хранить многие из тех других вещей, которыми я был:
function newButton( buttonId, offImg, onImg, group, on, off ) {
var b = document.getElementById(buttonId);
offImg = img( offImg );
onImg = img( onImg );
var btnImg = document.createElement('img');
btnImg.src = offImg.src;
b.appendChild( btnImg );
data( b.id, 'group', group );
var toggle = 0;
var click = function(event) {
switch(toggle) {
case 0:
turnGroupOff( data(this.id,'group') );
if( on(event) ) {
toggle = 1;
btnImg.src = onImg.src;
}
break;
case 1:
if( off(event) ) {
toggle = 0;
btnImg.src = offImg.src;
}
break;
}
}
b.onclick = click;
if( !isset(typeof window.button_groups[group]) )
window.button_groups[group] = [];
window.button_groups[group].push(b);
b = null;
}