我将一个键盘输入事件从document
委派给页面上的几个textarea
。因为我需要在按键输入上执行的操作是复杂,重复的,并且与该文本区域的文本内容相关,所以我发现最好将这些信息缓存在TextareaState
对象中(类似于{wasChanged: true, penultimateTextContents: "string", etc.}
),每个<textarea>
。永久存储该对象也将是有意义的,而不是每次触发输入事件时都重新计算它(无论如何重新计算都会删除以前的重要结果)。
起初,我想到了使用键-值对对象,但是DOM元素不能被替换为对象键。相反,我可以尝试为每个DOM元素生成一个unique CSS selector,然后使用该选择器作为对象键,但是这种方法看起来非常昂贵。
我的第三个也是最后一个方法是使用两个数组(var textareaElements = [], TextareaStateObjects = []
),然后每次触发输入处理程序时,我都可以TextareaStateObjects[textareaElements.indexOf(event.target)]
来获取相应的缓存对象。但是,考虑到.indexOf
会在每个输入事件中运行,这似乎仍然很昂贵,并且对于更大的数组长度而言,这将是非常昂贵的。
这里是否有更有效的解决方案?
答案 0 :(得分:2)
这里是否有更有效的解决方案?
是的,您的前两个解决方案之一。 :-)详细信息:
起初我想到使用键值对对象,但是DOM元素不能替换为对象键。
是的,但是它们可以是Map
对象键。如果您需要防止Map
将元素保留在内存中,它们甚至可以成为WeakMap
对象键。
您确实需要针对具有Map
和WeakMap
的环境(当前所有主要的环境都具有),尽管那里确实有一些功能非常强大的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)操作。