Settings

Theme

Why I Like Using Maps (and WeakMaps) for Handling DOM Nodes

macarthur.me

91 points by joelkesler 3 years ago · 29 comments

Reader

kevincox 3 years ago

I would argue that all "mapping" structures should use Map these days. Objects should be used only for record/struct data with a fixed set of programmer-named keys.

I think MDN has a good comparison: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

The only real downside of maps is that they don't support JSON serialization. However you can fix this pretty easily by using a map with an overridden `toJSON` for serializable keys.

  class StringMap extends Map {
    toJSON() {
          return Object.fromEntries(this);
    }
  }
  • taeric 3 years ago

    I'd say the big downside is that they don't support the general looking accessor that I'd wager way way way too many people will accidentally use. Myself definitely being one of them. Would have been better if they didn't "seem" to work with the wrong syntax, at least. But, alas, here we are.

    • kevincox 3 years ago

      I'm typically using TypeScript so that is more or less a non-issue. But even then it seems like an easy thing to get used to.

      • taeric 3 years ago

        Fair. And I didn't say it is a blocker. Just an easy footgun/downside. Is listed on the page linked, even.

  • panzerboiler 3 years ago

    That fix is not really a fix. The thing being broken for general serialization is JSON.

  • Kiro 3 years ago

    And how do deserialize it back to a StringMap without manually doing so? Being able to run a JSON.stringify and then JSON.parse on your state tree seamlessly is important imo.

  • orange8 3 years ago

    What is a "mapping" structure?

akira2501 3 years ago

Why not just use the 'dataset' property of the element itself? Then you can use querySelectorAll to find your selected rows automatically:

    node.querySelectorAll('tr[data-selected="true"]');
In other words.. "use the DOM to handle the DOM."
  • nirvdrum 3 years ago

    That'd work for this specific case, but I think that was just a simplified example. A Map can store a value of any type, while the dataset property will always be a string. If you're only working with strings, it's a great option since you don't need to maintain a side data structure at all. But, serializing and deserializing arbitrary objects would be expensive.

    • ZachSaucier 3 years ago

      Couldn't you do elementReference.foo = whateverTypeYouWant? No need to restrict things to just the data attribute.

      This wouldn't work if you need to know the order of the properties.

      • SSchick 3 years ago

        This generally doesn’t play nice with TS code bases and is generally considered an anti-pattern since there’s chances for collisions etc , you might get away using a unique symbol index but it’s still not good design.

        • rcfox 3 years ago

          I'm not sure about DOM elements, but tacking random new properties onto Javascript objects can cause them to become deoptimized by the runtime.

          • ZachSaucier 3 years ago

            Any reference for this?

            • nirvdrum 3 years ago

              I found [1] to be a pretty good summary of the key concepts that support the claim. The article, unfortunately, doesn't come right out and say it. But, it talks about how object shapes (a common JS object model) can be used as guards for inline caches (IC) to generate efficient machine code for property access. If you add a new property, you change the shape, which causes the cache guard to fail. That leads to deoptimization since the assumptions made about the call site when the code was compiled no longer hold. That call site is now either polymorphic or megamorphic, depending on the type of IC used and its level of polymorphism.

              [1] -- https://mathiasbynens.be/notes/shapes-ics

            • rcfox 3 years ago
        • megous 3 years ago

          Chances for collisions are pretty much nil if you use some prefix standard will never use, like $.

              el.$my_data = ...
          
          And in practice it works just fine.
geuis 3 years ago

This might be one of the more interesting articles I've read in a few years that deal with low level direct DOM manipulation vs "yet another thing about React".

  • thex10 3 years ago

    Yeah, I’ve too noticed how hard it is to find any web development guidance involving direct DOM manipulation now!

    I was recently refactoring some code and wondering whether to use a (JS) class… The search results were mostly about React Class Components. And I was contemplating building a reusable component… results from the past decade were split between React Components and Web components. (In retrospect I should’ve tried DDG or something not connected to my search history lol).

    Anyway where are the vanilla JS/TS DOM manipulators blogging these days? I guess here is one, thankful for the post :)

globalise83 3 years ago

Nice! I have used Maps often for storing arbitrary key-value pairs, but until 5 minutes ago I didn't know you could use object references as keys. So this was a very good use of 5 mins.

  • beebeepka 3 years ago

    Same applies to Set, too. You can store an object, or instance, then use "has" to check for it.

rektide 3 years ago

I need to read & consider this more, but my word, putting data in the DOM is so divine, is so the web way. See, HTML is the Web, https://www.petelambert.com/journal/html-is-the-web

  • incrudible 3 years ago

    Not for me. The DOM is virtually always a poor fit for your view model. You will need to abstract it to stay sane, sooner or later. Manipulating DOM is slow and fraught with performance cliffs.

    In my opinion, DOM nodes should be disposable. A means to display data through the browser, not more. I avoid storing custom data on them, I avoid association like in the blog post. 98% of the time, you just want a simple transform from domain data into DOM nodes, for display. You really don't want to care which DOM nodes. This is how React works, and the reason why it is successful.

    Granted, once you get into the hundreds of elements updated at interactive rates, you may have to mess with the DOM directly, because you can't trust React to be smart about it. That's still just a consequence of DOM being terribly inefficient for historical reasons.

JoeSloth 3 years ago

This is great for some use cases, but in situations like a very large list mentioned in the article it’s likely that a real world app would be virtualizing the nodes.. which would make an id a more stable key than a dom node.

eviks 3 years ago

Why did they not make the map assigning syntax as convenient as with objects, instead going for the clunky get/set?

  • panzerboiler 3 years ago

    Because a Map is an Object. It has properties and methods. The only way to provide a safe and clean namespace for your keys is to provide an explicit API. It also supports keys that can be not only strings or symbols.

  • beebeepka 3 years ago

    Several things such as standard way for adding, removing and checking for keys, built-in size property, extensibility.

    You think "key in object" is better than "maaaaap.has(key)"? I don't because it's unpredictable and prone to exceptions.

    Give them a try. It's not a complete replacement for regular old objects

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection