【问题标题】:Best way to store (and retrieve!) dom-element-specific data using javascript使用 javascript 存储(和检索!)特定于 dom 元素的数据的最佳方式
【发布时间】:2011-09-27 23:05:57
【问题描述】:

我有一个下拉列表矩阵,我希望能够在用户视觉编辑设置时向他们显示(例如,通过将表格单元格的背景设置为红色)。如果他们将其切换回原始值,它将恢复为默认背景颜色。这样一来,系统的状态就很清楚了。

为此,我想出了在<select> 元素上设置onchange 处理程序,如下所示:select.setAttribute('onchange','chSel(this,'+select.selectedIndex+');'); 其中select 是<select> 元素。

它将调用函数chSel并引用自身和当前设置,因此它将能够设置将通过CSS确定显示属性的类:

function chSel(node, origSelIndex) {
    if (node.selectedIndex == origSelIndex) // switching back to the set state
    node.parentNode.removeAttribute('class');
    else node.parentNode.setAttribute('class','cell_changed');
}

这不需要检索动态数据,尽管onchange 处理程序中保存的selectedIndex 值用作特定于元素的静态数据。

我想通过在我的表格周围制作一个边框来扩展我所描述的这个功能,我打算在它的任何内容被修改时将其涂成红色,这样如果表格很大,仍然可以一目了然如果它的任何条目被修改。

有一种不好的方法和更好的方法来实现这一点。我确实知道如何在兼容的 HTML/XHTML 中实现的坏方法是让chSel(在用户与下拉菜单交互时执行)运行完整搜索整个表格来计算编辑,并相应地标记表格边框。这实际上可能不是性能问题,但我预计它会是。我认为它的扩展性不会那么好。

更好的方法是计算已编辑元素的数量。当该数字回落到零(用户将他的所有编辑更改回原始值)时,我可以将表格重新标记为未编辑。

但是,这需要我有一些方法来确定选择菜单是否已从新值更改为不同的新值,而不是从原始值更改为新值,即现在与被改变的值是。似乎没有办法做到这一点。 This discussion 似乎表明我无法在保持文档有效的同时创建和检索自定义属性。当我试图分配任意属性时,我很难检索它们的值。

还剩下什么?我可以构建一个对象,将其用作从元素到整数的散列吗? javascript甚至会让我这样做吗?

【问题讨论】:

  • 不要使用 setAttribute 设置监听器,因为它在某些浏览器中不起作用,使用 addEventListener/attachEvent 或仅将函数分配给适当的属性。
  • @RobG,感谢您的提示。将 HTML 内容与代码语义分开可能是一个非常好的主意。不过,您知道哪些浏览器会出现问题吗?

标签: javascript html


【解决方案1】:

我不确定我是否完全按照您的想法进行操作,但您可以在 javascript 变量或自定义 DOM 属性中跟踪状态。 Javascript 变量稍微容易一些,但是你必须给它们一个适当的范围,这样它们才能在你的操作期间存活下来。自定义 DOM 属性有时更容易实现面向对象并避免任何全局 javascript 变量。 jQuery 的.data() 功能实际上介于两者之间。它使用一个自定义数据属性用唯一标签标记 DOM 对象,然后将所有实际数据存储在 javascript 映射中。

一旦您适当地存储了状态,每当您收到更改事件时,您都可以与之前的值进行比较并查看与之前的状态相比实际发生了什么变化,更新您的新状态并将新值保存为之前的值或执行任何操作否则你需要做的。

例如,通过这种方式,您可以计算有多少已编辑项目,并且无论何时该值从 0 变为非零或非零变为 0,您都可以根据需要更改整个表格的外观。同样,只要单个单元格发生变化,您就可以更新它的外观。

如果您的 DOM 元素是您唯一的数据结构,那么创建自定义属性并在其中保留其他数据可能是最简单的。这样做的 HTML5 方法是在您的属性名称前添加“data-”,如 obj.setAttribute("data-origSelectIndex", x) 或 obj["data-origSelectIndex"] = x 中的,以防止它们与标准属性发生任何名称冲突。

如果出于某种原因您不想使用自定义属性,那么您需要创建一个并行的 javascript 数据结构(可能是一个数组,其中数组中的每个项目以某种方式对应于一个 DOM 元素,您可以映射两者之间)保存数据。

【讨论】:

  • 好的,但是对于我要跟踪的每个项目,我没有一个 javascript 变量。我使用 DOM 元素作为我的数据结构。 edit:好的,有没有一些简单的方法可以在没有 jquery 的情况下构建它?我不想为一件事引入这种依赖
  • 然后使用 setAttribute 和 getAttribute 来保存 DOM 对象的状态。我没有遵循您要做得足够好的事情来知道如何推荐特定代码。如果您出于某种原因不想使用自定义属性,那么您必须声明一个并行的 javascript 数据结构(可能是一个数组),该结构可以保存表中每个对象的数据。
  • 如下所示,将内容存储在对象中可能比 data- 属性更好。只需给每个控件一个 id 以供参考并将值写入对象(比使用 getAttribute、解析值、构建新值,然后使用 setAttribute 更快更简单)。您为每种控件类型存储的内容是不同的,例如如果您使用选择,则只需跟踪所选索引的值。对于文本输入,您需要存储整个值。
  • 是的,根据reference.sitepoint.com/javascript/Element/getAttribute getAttribute 在所有浏览器中都有错误,尽管不是这样:quirksmode.org/dom/w3c_core.html#t113 我只是要避免getAttribute。
  • @Steven - 是的,好主意。 DOM 属性在浏览器之间更加一致。如果您将内容存储在一个对象中,您可以更直接、更快速地定义结构和访问值。
【解决方案2】:

大多数表单控件都有一个 defaultValue 属性。要确定是否有任何改变了它们的默认值,请循环浏览它们并将当前的 value 与 defaultValue 进行比较。对于select 元素,始终将selected 设置为1,并检查当前选中的option 是否将其defaultSelected 属性设置为true。单选按钮和复选框也是如此。

这样您就不必存储任何值,它由 DOM 元素本身完成。

【讨论】:

  • 好吧,可能有一些原因我想设置这些,但它们不是自动设置的,这很糟糕。不过很高兴知道这一点,所以下次我不需要重新发明轮子了。此外,这些仍然无法让我知道我从切换了哪个选项。
  • 上述内容是否有用当然取决于您要做什么。与默认值或选定值或其他值进行比较会表明它与默认值不同,但仅此而已。这通常就足够了。如果您尝试跟踪值的每次更改,那么您将需要多种方法,并且可能最终在某些浏览器中失败,因为当用户可以输入数据并以多种方式更改值时,跟踪更改非常困难。
【解决方案3】:

根据@jfriend00 的建议,使用 dom 属性作为一种全局变量:

Jsfiddle demo.

CSS

.selected { border: 1px solid red;}

HTML

<div id="table1">
    <select id="sel1">
        <option>1</option>
        <option>2</option>
        <option selected>3</option>
    </select>
    <select id="sel2">
        <option selected>1</option>
        <option>2</option>
        <option>3</option>
    </select>
</div>
<div id="table2">
    <select id="sel3">
        <option>1</option>
        <option selected>2</option>
        <option>3</option>
    </select>
    <select id="sel4">
        <option selected>1</option>
        <option>2</option>
        <option>3</option>
    </select>
</div>

Javascript:

(function(){
    var selects = document.getElementsByTagName('select'),
        i = 0,
        selectLength = selects.length;

    var applyParentChange = function(node, val){
        var currentEdits = node.getAttribute('data-edits') || 0;
        currentEdits = parseFloat(currentEdits, 10) + val;
        if(currentEdits <= 0) {
            currentEdits = 0;
            node.className = '';
        } else {
            node.className = 'selected';
        }

        node.setAttribute('data-edits',currentEdits);

    };

    var onSelectChange = function(){
        var defaultIndex = parseFloat(this.getAttribute('data-default'),10);
        if(defaultIndex === this.selectedIndex){
            applyParentChange(this.parentNode, -1);
            this.className = '';
        } else {
            applyParentChange(this.parentNode, 1);
            this.className = 'selected'; 
        }

    };

    for(i=0; i<selectLength; i+=1){
        selects[i].setAttribute('data-default',selects[i].selectedIndex);
        selects[i].onchange = onSelectChange;
    }
})();

稍微浏览一下代码...

您将获得所有select 元素的集合,并为它们提供一个具有默认选定索引的自定义属性,然后直接为其提供onchange 事件。

在更改时,它会检查新索引与原始(默认)索引并在需要时将 CSS 应用于元素。然后它将它传递给父节点(在本例中为 div,但可以是任何值)。

在父级中,我们为编辑次数保留了一个类似的自定义属性。如果有人一遍又一遍地重新选择默认值,则最小值 0 将保持设置。如果有 1 个或多个编辑,则应用 CSS,如果为 0,则删除。

请注意,这只是一种方法,您可以将集合保存在保存引用/计数的变量中,有些使用隐藏(屏幕外)输入元素来存储值...

【讨论】:

  • 我认为使用数据属性是浪费时间,只需将值存储在一个对象中。数据属性可能更多是供服务器向客户端发送特定值而不是供客户端操作。
  • 我同意,我想我会给出一些示例代码,因为它被标记为答案。我宁愿在代码中创建元素,并通过某个模块系统通过更改事件保持原始值,但如果真的想使用data-,可以。
猜你喜欢
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多