在JavaScript中能否获得对唯一DOM元素的唯一引用(用作对象中的唯一键)?

时间:2019-01-26 01:22:51

标签: javascript html dom reference

在尝试创建自己的双向数据绑定方法时,我认为我有个好主意。作为该想法的一部分,我尝试使用DOM元素引用作为JavaScript对象中的键,该对象将保存与每个元素相关的数据。

不幸的是,似乎JavaScript对DOM元素的引用是通用的(关于tagName [如p])。

例如:

function testScenarios(){
  // create references to DOM objects
  var a = document.getElementById("id_a"),
      b = document.getElementById("id_b");
  
  // create and fill a test object
  var testObj = {};
  testObj[a] = "a"; // use the reference to p#id_a as a key for the value "a"
  testObj[b] = "b"; // use the reference to p#id_b as a key for the value "b"
  testObj[a.id] = "aId"; // use the id captured from the reference to p#id_a as a key for the value "aID"
  testObj[b.id] = "bId"; // use the id captured from reference to p#id_b as a key for the value "bId"
  testObj["0"] = a;  // use "0" as a key for a value that is the reference to p#id_a
  testObj["1"] = b; // use "1" as a key for a value that is the reference to p#id_b

  // store test values
  var testResults = {
    does_a_equal_b : (a===b),
    does_aId_equal_bId : (a.id===b.id),
    does_objA_equal_objB : (testObj[a]===testObj[b]),
    does_objAid_equal_objBid : (testObj[a.id]===testObj[b.id]),
    does_obj0_equal_obj1 : (testObj["0"]===testObj["1"]),
    what_is_a : a,
    what_is_b : b
  }
  
  // run the tests and display results
  for(testKey in testResults){
    document.getElementById(testKey).innerHTML = testResults[testKey];
  }
  document.getElementById("the_object").innerHTML = JSON.stringify(testObj, null, 4);
}
p {width:400px;}
p:nth-child(odd){background:#ccc;}
span {float:right; font-weight:800;}
p:nth-child(8){background:#c63;}
p:nth-child(10){background:#c63; height:40px;}
p:nth-child(11){background:#c63; height:40px;}
p:nth-child(12){background:#c63; height:60px;}
<p id="id_a">paragraph element a</p>
<p id="id_b">paragraph element b</p>
<button onclick="testScenarios()">Run the tests</button>
<p>a === b? (expecting false)<span id="does_a_equal_b"></span></p>
<p>a.id === b.id? (expecting false)<span id="does_aId_equal_bId"></span></p>
<p>testObj[a.id] === testObj[b.id]? (expecting false)<span id="does_objAid_equal_objBid"></span></p>
<p>testObj["0"] === testObj["1"]? (expecting false)<span id="does_obj0_equal_obj1"></span></p>
<p>testObj[a] === testObj[b]? (expecting false)<span id="does_objA_equal_objB"></span></p>
<p>ok, but... why?</p>
<p>What is a? (expecting somethihng like p#id_a, or a number)<span id="what_is_a"></span></p>
<p>What is b? (expecting somethihng like p#id_a, or a number)<span id="what_is_b"></span></p>
<p>What is testObj?<span id="the_object"></span></p>
<p>Sooooo... even though:<br>a !== b,<br>testObj[a] === testObj[b].<br>?¯\_(ツ)_/¯?<br><br>Is there some way to get the unique aspects of references a and b out of those references for later use?</p>

是否有一种方法来获取与对DOM对象的每次引用相关联的唯一标识符?

3 个答案:

答案 0 :(得分:1)

JavaScript对象键始终是 strings ,因此,如果a是元素,testObj[a]将不起作用,因为该元素将被强制转换为字符串,这可能与其他元素的强制字符串具有相同的值。请改用Map,它类似于对象,但是可以具有任何值的键,包括HTMLElements

// create references to DOM objects
var a = document.getElementById("id_a"),
  b = document.getElementById("id_b");

// create and fill a test object
const map = new Map();
map.set(a, 'a');
map.set(b, 'b');
map.set(a.id, "aId");
map.set(b.id, "bId");
map.set('0', a);
map.set('1', b);
map.set(a.id, "aId");

// store test values
var testResults = {
  does_a_equal_b: (a === b),
  does_aId_equal_bId: (a.id === b.id),
  does_objA_equal_objB: (map.get(a) === map.get(b)),
  does_objAid_equal_objBid: (map.get(a.id) === map.get(b.id)),
  does_obj0_equal_obj1: (map.get('0') === map.get('1')),
  what_is_a: a,
  what_is_b: b
};
console.log(testResults);
<p id="id_a">paragraph element a</p>
<p id="id_b">paragraph element b</p>

答案 1 :(得分:1)

WeakMap确实很方便。

与Map相似,但是在元素被破坏时将被删除并收集垃圾。

下面是一个简单的工作片段。出于测试目的,我只是创建一个附加到元素的简单生成的字符串。然后从点击处理程序中,我正在读回此信息。对于更复杂的数据结构,该值也可以是对象。

const data = new WeakMap();

let count = 0;
for (const el of document.querySelectorAll("div")) {
  count += 1;
  data.set(el, `This is data for item ${count}`);
}

document.body.addEventListener("click", evt => {
  const info = data.get(evt.target);
  if (info) document.querySelector("#clicked").innerText = info;
});
div { 
  border: 1px solid black; 
  padding: 4px;
  margin: 4px;
}
<div id="items">
  <div>one</div>
  <div>two</div>
  <div>three</div>
  <div>four</div>
</div>
<div id="clicked"></div>

答案 2 :(得分:0)

您可以遍历DOM并为所有内容添加唯一的类名。

j = 0;
for (let i = 0; i < document.body.childNodes.length; i++) {
  document.body.childNodes[i].classList.add("__j" + j++); 
}

您将要递归地这样做,以生下孩子。