我想写一些纯JavaScript,以便更好地理解它(我知道在这框架,比如jQuery的“真正实践”是更建议和适用的,但这不是真正关于如何使用框架,更多关于纯JavaScript如何工作和最佳实践)。如何编写围绕DOM存储数据(对象,自定义属性)节点
反正我写了一些简单的javascript代码。我想创建一组按钮,每次只有一个状态从set {on,off}开始,每个状态都会映射到相应的函数,在进入该状态时被触发。主组中的每组按钮一次只能包含一个处于打开状态的按钮。这个概念与单选按钮的概念相似。 为什么不使用单选按钮呢?在语义上,它只是假设是某些控件元素的按钮,但是我想我可以拥有任何一种方式,但问题并不在于此。
问题是,为了解决这个问题,我在我的Javascript中通过id
向特定的button
元素添加了很多自定义属性。我正在做一些研究,发现这个question和question,关于在DOM节点(对象)上使用自定义属性。他们似乎主张反对这种做法,甚至可以说这样做可能导致潜在的内存泄漏,这取决于浏览器的实现。
然而,对于每个按钮创建我需要保持大量的属性的跟踪,如果我扩大这个剧本我可能会更增加。那么将它们存储在DOM节点上的最好方法是什么,但是仍然保持跟踪所有内容,并且能够在附加函数中使用等。
这是不容易明显,我如何做到这一点而不在最小存储井名隔开对象到DOM节点button
元素的引用。
我可以看到,从这个question 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的具体做法有个很好的想法(随机命名的属性存储缓存索引整数)。
我事先知道哪些主机元素需要扩展,以及如何;我也可以/想在HTML端设置一个id
属性。
所以,通过了jQuery设置的启发,我决定也创建一个全局缓存变量,除了我将使用DOM节点的id属性为我的缓存键。由于它应该是一个唯一的标识符(根据定义),并且我没有计划动态改变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>
UPDATE 2
我发现,通过使用闭合的功率I真正只需要存储一个“特殊”属性,即该组的按钮曾经属于。
我改变了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;
}
jQuery在JavaScript中是_written_。看看[他们是怎么做的](https://github.com/jquery/jquery/blob/master/src/data.js),特别是看看'data()'函数。 – voithos 2012-03-16 16:41:21