如何有效地将DOM元素与其对应的对象关联存储?

时间:2018-07-12 11:01:51

标签: javascript html

我将一个键盘输入事件从document委派给页面上的几个textarea。因为我需要在按键输入上执行的操作是复杂,重复的,并且与该文本区域的文本内容相关,所以我发现最好将这些信息缓存在TextareaState对象中(类似于{wasChanged: true, penultimateTextContents: "string", etc.} ),每个<textarea>。永久存储该对象也将是有意义的,而不是每次触发输入事件时都重新计算它(无论如何重新计算都会删除以前的重要结果)。

起初,我想到了使用键-值对对象,但是DOM元素不能被替换为对象键。相反,我可以尝试为每个DOM元素生成一个unique CSS selector,然后使用该选择器作为对象键,但是这种方法看起来非常昂贵。

我的第三个也是最后一个方法是使用两个数组(var textareaElements = [], TextareaStateObjects = []),然后每次触发输入处理程序时,我都可以TextareaStateObjects[textareaElements.indexOf(event.target)]来获取相应的缓存对象。但是,考虑到.indexOf会在每个输入事件中运行,这似乎仍然很昂贵,并且对于更大的数组长度而言,这将是非常昂贵的。

这里是否有更有效的解决方案?

2 个答案:

答案 0 :(得分:2)

  

这里是否有更有效的解决方案?

是的,您的前两个解决方案之一。 :-)详细信息:

  

起初我想到使用键值对对象,但是DOM元素不能替换为对象键。

是的,但是它们可以是Map对象键。如果您需要防止Map将元素保留在内存中,它们甚至可以成为WeakMap对象键。

您确实需要针对具有MapWeakMap的环境(当前所有主要的环境都具有),尽管那里确实有一些功能非常强大的polyfill在做一些酷炫的事情来模仿Map的行为以一种聪明,表现良好的方式。

示例:

// See https://stackoverflow.com/questions/46929157/foreach-on-queryselectorall-not-working-in-recent-microsoft-browsers/46929259#46929259
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
    // Yes, there's really no need for `Object.defineProperty` here
    NodeList.prototype.forEach = Array.prototype.forEach;
}

const map = new WeakMap();
document.querySelectorAll("textarea").forEach((element, index) => {
  ++index;
  map.set(element, {name: "TextArea #" + index});
});

document.addEventListener("click", event => {
  const entry = map.get(event.target);
  if (entry) {
    console.log("The name for the textarea you clicked is " + entry.name);
  }
});
<div>Click in each of these text areas:</div>
<textarea>one</textarea>
<textarea>two</textarea>

  

我可以尝试为每个DOM元素生成一个唯一的CSS选择器,然后将该选择器用作对象键,但是这种方法看起来非常昂贵。

如果您使用id则不是:给自己一个唯一的前缀,然后在其后面加上一个不断增加的数字。 (而且,如果您需要从ID转到元素,getElementById盲目。)

示例:

// See https://stackoverflow.com/questions/46929157/foreach-on-queryselectorall-not-working-in-recent-microsoft-browsers/46929259#46929259
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
    // Yes, there's really no need for `Object.defineProperty` here
    NodeList.prototype.forEach = Array.prototype.forEach;
}

let nextIdNum = 1;
const pseudoMap = Object.create(null);
document.querySelectorAll("textarea").forEach((element, index) => {
  if (!element.id) {
    element.id = "__x_" + nextIdNum++;
  }
  ++index;
  pseudoMap[element.id] = {name: "TextArea #" + index};
});

document.addEventListener("click", event => {
  const entry = pseudoMap[event.target.id];
  if (entry) {
    console.log("The name for the textarea you clicked is " + entry.name);
  }
});
<div>Click in each of these text areas:</div>
<textarea>one</textarea>
<textarea>two</textarea>

如果将id用于其他目的,它可以是data-*属性。 (而且,如果您需要从data-*属性值转到元素,则querySelector属性上的data-*并不那么昂贵。)

示例:

// See https://stackoverflow.com/questions/46929157/foreach-on-queryselectorall-not-working-in-recent-microsoft-browsers/46929259#46929259
if (typeof NodeList !== "undefined" && NodeList.prototype && !NodeList.prototype.forEach) {
    // Yes, there's really no need for `Object.defineProperty` here
    NodeList.prototype.forEach = Array.prototype.forEach;
}

let nextIdNum = 1;
const pseudoMap = Object.create(null);
document.querySelectorAll("textarea").forEach((element, index) => {
  const id = "__x_" + nextIdNum++;
  element.setAttribute("data-id", id);
  pseudoMap[id] = {name: "TextArea #" + index};
});

document.addEventListener("click", event => {
  const entry = pseudoMap[event.target.getAttribute("data-id")];
  if (entry) {
    console.log("The name for the textarea you clicked is " + entry.name);
  }
});
<div>Click in each of these text areas:</div>
<textarea>one</textarea>
<textarea>two</textarea>

答案 1 :(得分:1)

您可以在每个输入上分配唯一的ID,然后创建一个数组,其中每个键的索引用于访问数据的缓存版本。

var cachedData=[];
cachedData[someElement.id] = someValue;

这将是用于读取/写入索引值的O(1)操作。