0

Edit: turns out nothing is actually wrong with the second snippet (my real code). On one page it works, and on another it doesn't. Yea for underlying errors.

I'm creating a DOM element and giving that DOM element to a WeakMap as a key. Then, with JQuery event delegation/event listener, I'm trying to retrieve the saved key but it's returning undefined:

const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"})
document.body.appendChild(item)

// later on in event delegation
$("div").on("click", function(event) {
const target = event.target, data = __data.get(target);
console.log(data)
// prints undefined

Anyone know what's wrong or an alternative method to save data for a DOM element that doesn't have an ID?

Edit: I'm kinda annoyed that the example I made works but my own code doesn't... (some bits look redundant. This is modeled after my actual code, so not all the missing pieces are here, just pragmatically) but here's the apparently working code:

const __data = new WeakMap();

function buildingItem() {
  const item = document.createElement("div");
  item.setAttribute("data-action", "delete");
  __data.set(item, {hi: 123});
  return item;
}

function build() {
  const main = document.getElementById("main")
  for (let i = 0; i < 3; i++) {
    const container = document.createElement("div"), attached = document.createElement("div");
    const build = buildingItem(),
      data = __data.get(build);
    build.classList.add("classified");
    data["hello"] = `Item ${i}`
    __data.set(build, data);
    build.innerText = `Item ${i}`
    attached.append(build);
    container.append(attached);
    main.append(container);
  }
}
build()
$(document).on("click", "div.classified[data-action]", function(event) {
const target = event.currentTarget, data = __data.get(target);
console.log(`CTarget Data: ${data["hello"]}`)
})
<div id="main"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
acw
  • 503
  • 1
  • 9
  • Would it bei an option to attach an ID as data-* attribute to the event target? – Jankapunkt Dec 27 '20 at 17:16
  • `$("div").on("click", ...)` will fire for *any* `
    ` on the page. There's not enough information here to provide a good answer.
    – Pointy Dec 27 '20 at 17:17
  • 2
    This should work. Please check whether `item === target`. Can you provide a [mcve] please? – Bergi Dec 27 '20 at 17:18
  • [I can reproduce it here](https://jsbin.com/vijiredexe/1/edit?html,js,console,output). Single `
    ` on the page clicking it definitely produces the div tag as a target and yet it fails to look it up in the weak map.
    – VLAZ Dec 27 '20 at 17:21
  • @Bergi Pointy apologies. Didn't realize it still wasn't enough. But The example VLAZ made does seem to reproduce it. – acw Dec 27 '20 at 17:23
  • 1
    Just a side note: *"later on in event delegation"* There isn't any event delegation in that code. Just event *handling*. – T.J. Crowder Dec 27 '20 at 17:26
  • 1
    @VLAZ - The reason you're seeing it there is that the `div` with `Click me` isn't the same `div` as the one that's a key in the map (#2 in the answer below). [Here's a version](https://jsbin.com/kanowimena/1/edit?html,js,console,output) adding height to the `div` that's used as a key (along with a background color) so it's possible to click it. – T.J. Crowder Dec 27 '20 at 17:26
  • Your update to the question just now means that the click handler will never fire, because A) You don't put the necessary class and `data-action` attribute on the `div`, and B) You're adding the `div` to `body`, but then your delegation code expects to find it **inside** another `div`. Please update your question with a **runnable** [mcve] demonstrating the problem using Stack Snippets (the `[<>]` toolbar button); [here's how to do one](https://meta.stackoverflow.com/questions/358992/). But it'll almost certainly be one of the two things I've identified in my answer. – T.J. Crowder Dec 27 '20 at 17:41
  • Sorry will update later. Didn't expect you to come back so soon. – acw Dec 27 '20 at 17:43
  • 1
    I've rolled back the edit, since it encouraged people to answer with "you don't have that `div` inside a `div`" or similar. – T.J. Crowder Dec 27 '20 at 17:59
  • @T.J.Crowder So I've made a quick example... that works... I'm kinda reluctant to post the actual code I have, but the example is modeled from my actual code. Maybe it's some underlying error? – acw Dec 27 '20 at 18:14
  • Turns out something else is causing this because on one page it works and on another it doesn't . – acw Dec 27 '20 at 18:23

1 Answers1

1

Two possible issues:

  1. target is the innermost element that was clicked. You probably want this or event.currentTarget instead, which is the element on which you hooked the event handler (which may be an ancestor element to target).

  2. Your jQuery code hooks up the click event on all div elements, not just that one, but you only have that one div in the WeakMap. If you click a different div, you'll naturally get undefined because that other div isn't a key in the map.

Here's an example (I've added a span within the div we have in the map to demonstrate #1, and also added a second div to demonstrate #2):

const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"});
document.body.appendChild(item);
item.insertAdjacentHTML("beforeend", "<span>Click me, I'll work</span>");
document.body.insertAdjacentHTML("beforeend", "<div>Click me, I won't work (I'm not a key in the map)</div>");

$("div").on("click", function(event) {
    const target = event.currentTarget, data = __data.get(target);
    console.log("with currentTarget:", data);
    
    // Note that using `event.target` wouldn't hav eworked
    console.log("with target:", __data.get(event.target));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

You've mentioned that in your real code you're using event delegation. currentTarget and this are both fine in that case as well:

// Event delegation
$(document.body).on("click", "div.example", function(event) {
    const data1 = __data.get(event.currentTarget);
    console.log("using currentTarget:", data1);
    const data2 = __data.get(this);
    console.log("using this:", data2);
});

// Adding the relevant `div`
const item = document.createElement("div"), __data = new WeakMap();
__data.set(item, {hello: "123"});
item.className = "example";
item.textContent = "Some text to make div clickable";
document.body.appendChild(item);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
T.J. Crowder
  • 879,024
  • 165
  • 1,615
  • 1,639
  • "If you click a different div, you'll naturally get undefined because that other div isn't a key in the map." Your second point makes sense. I didn't think the event delegation would make it that way. I tried just adding an event listener, but it wasn't actually binding, so I resorted to delegating it. Any suggestions then? I believe what @VLAZ made makes this reproducible: https://jsbin.com/vijiredexe/1/edit?html,js,console,output – acw Dec 27 '20 at 17:25
  • Is it helpful to point out that these don't have an attached IDs? I used the div element because it was the first element that popped in my mind. In actuality, it's a dropdown a element from bootstrap like `.on("click", "a.dropdown-item[data-action], function(event))` but I suppose that doesn't make a different in this case. – acw Dec 27 '20 at 17:27
  • @acw - VLAZ's example is an example of #2. Since the `div` you're adding doesn't have anything in it, it's of zero height, and so you can't click it -- but VLAZ's example has a **different** `div` with text in it. If we add height to the div you're adding, it's possible to click it: [Here's VLAZ's example](https://jsbin.com/kanowimena/1/edit?html,js,console,output) with both height and background color added to the `div` you're using as a key in the map. And it's absolutely fine there aren't any `id`s. :-) (See also my comment on the question. There's no event delegation in your code.) – T.J. Crowder Dec 27 '20 at 17:29
  • If you wanted event delegation, you'd use the three-argument version of `on`: `$(document.body).on("click", "a.dropdown-item[data-action]", function(event) { /*...*/ });` (The parent element doesn't have to be `body`, just a container you're going to add the other elements to.) – T.J. Crowder Dec 27 '20 at 17:31
  • Ah sorry for terminology. New at this. I've tried using `this`, `currentTarget`, and `target`, all of which have data be undefined. So I assume it's just about point #2? – acw Dec 27 '20 at 17:32
  • That's actually what I'm currently doing, but it's not working (poor code in question on my part) with the `event.currentTarget`. Updated code in question to show the "div.classname[data-action]" was used just not working. Will ping with reproducible code later. – acw Dec 27 '20 at 17:33
  • @acw - That's the most likely thing, that you're probably clicking a different `div`. If you update the question with a [mcve], we can tell you for sure, but that's probably what's happening. – T.J. Crowder Dec 27 '20 at 17:33
  • 1
    @acw - I've added an example using event delegation, just for the avoidance of doubt. :-) – T.J. Crowder Dec 27 '20 at 17:39
  • Going to accept the answer for now. It should help any other folks with an issue similar to the one I didn't write correctly. I think there's gotta be some underlying errors that's causing my production code to error though... – acw Dec 27 '20 at 18:17
  • I'm actually done. Ok it's gotta be my code, because on one page it works and on another it doesn't. – acw Dec 27 '20 at 18:22