{"id":1551,"date":"2024-07-29T07:47:16","date_gmt":"2024-07-29T12:47:16","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=1551"},"modified":"2024-07-30T12:31:28","modified_gmt":"2024-07-30T17:31:28","slug":"patterns-for-memory-efficient-dom-manipulation","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/patterns-for-memory-efficient-dom-manipulation\/","title":{"rendered":"Patterns for Memory Efficient DOM Manipulation with Modern Vanilla JavaScript"},"content":{"rendered":"\n<p>Let&#8217;s continue the modern vanilla JavaScript series!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"488\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?resize=1024%2C488&#038;ssl=1\" alt=\"Memory Efficient DOM Manipulation\" class=\"wp-image-1558\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?resize=1024%2C488&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?resize=300%2C143&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?resize=768%2C366&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?resize=1536%2C732&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/hero-1.jpg?w=1792&amp;ssl=1 1792w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-todomvc\/\">Writing a TodoMVC App with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-reactivity\/\">Patterns for Reactivity with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/patterns-for-memory-efficient-dom-manipulation\/\">Patterns for Memory Efficient DOM Manipulation with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<p>I&#8217;ll discuss best practices to avoid excess memory usage when managing updating the DOM to make your apps <a href=\"https:\/\/frontendmasters.com\/courses\/blazingly-fast-js\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">blazingly fast\u2122\ufe0f<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">DOM: Document Object Model \u2013&nbsp;A Brief Overview<\/h2>\n\n\n\n<p>When you render HTML, the live view of those rendered elements in the browser is called the DOM. This is what you&#8217;ll see in your developer tools &#8220;Elements&#8221; inspector:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"310\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/DOM.jpg?resize=1024%2C310&#038;ssl=1\" alt=\"Elements panel in chrome dev tools\" class=\"wp-image-1560\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/DOM.jpg?resize=1024%2C310&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/DOM.jpg?resize=300%2C91&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/DOM.jpg?resize=768%2C233&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/DOM.jpg?w=1250&amp;ssl=1 1250w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>It&#8217;s essentially a tree, with each element inside of it being a leaf. There is an entire set of APIs specifically dealing with modifying this tree of elements.<\/p>\n\n\n\n<p>Here&#8217;s a quick list of common DOM APIs: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>querySelector()<\/code><\/li>\n\n\n\n<li><code>querySelectorAll()<\/code><\/li>\n\n\n\n<li><code>createElement()<\/code><\/li>\n\n\n\n<li><code>getAttribute()<\/code><\/li>\n\n\n\n<li><code>setAttribute()<\/code><\/li>\n\n\n\n<li><code>addEventListener()<\/code><\/li>\n\n\n\n<li><code>appendChild()<\/code><\/li>\n<\/ul>\n\n\n\n<p>These are attached to the <code>document<\/code>, so you use them like <code>const el = document.querySelector(\"#el\");<\/code>. They are also available on all other elements, so if you have an element reference you can use these methods and their abilities are scoped to that element.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> nav = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#site-nav\"<\/span>);\n<span class=\"hljs-keyword\">const<\/span> navLinks = nav.querySelectorAll(<span class=\"hljs-string\">\"a\"<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>These methods will be available in the browser to modify the DOM, but they won&#8217;t be available in server JavaScript (like <a href=\"https:\/\/nodejs.org\/en\">Node.js<\/a>) unless you use a DOM emulator like <a href=\"https:\/\/github.com\/jsdom\/jsdom\">js-dom<\/a>.<\/p>\n\n\n\n<p>As an industry, we&#8217;ve offloaded most of this direct rendering to frameworks. All JavaScript frameworks (React, Angular, Vue, Svelte, etc) use these APIs under the hood. While I recognize that the productivity benefits of frameworks often outweigh the potential performance gains of manual DOM manipulation, I want to demystify what goes on under the hood in this article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why manipulate the DOM yourself in the first place?<\/h2>\n\n\n\n<p>The main reason is performance. Frameworks can add unnecessary data structures and re-renders leading to the dreaded stuttering \/ freezing behavior seen in many modern web apps. This is due to the Garbage Collector being put on overdrive having to handle all that code.<\/p>\n\n\n\n<p>The downside is it is more code to handle DOM manipulation yourself. It can get complicated, which is why it a better developer experience to use frameworks and abstractions around the DOM rather than manipulating the DOM manually. Regardless, there are cases where you may need the extra performance. That is what this guide is for.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">VS Code is Built on Manual DOM Manipulation<\/h3>\n\n\n\n<p>Visual Studio Code is one of those such cases. VS Code is <a href=\"https:\/\/www.youtube.com\/watch?v=gnKzJRr-rd0\">written in vanilla JavaScript<\/a> &#8220;to be as close to the DOM as possible.&#8221; Projects as large as VS Code need to have tight control over performance. Since much of the power is in the plugins ecosystem, the core needs to be as core and lightweight as possible and is responsible for its wide adoption.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"What frontend framework does VS Code use?\" width=\"500\" height=\"281\" src=\"https:\/\/www.youtube.com\/embed\/gnKzJRr-rd0?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>Microsoft <a href=\"https:\/\/thenewstack.io\/from-react-to-html-first-microsoft-edge-debuts-webui-2-0\/\">Edge also recently moved off of React<\/a> for the same reason.<\/p>\n\n\n\n<p>If you find yourself in this case where you need the performance of direct DOM manipulation \u2013 lower level programming than using a framework \u2013 hopefully this article will help!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tips for More Efficient DOM Manipulation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Prefer hiding\/showing over creating new elements<\/h3>\n\n\n\n<p>Keeping your DOM unchanged by hiding and showing elements instead of destroying and creating them with JavaScript is always going to be the more performant option.<\/p>\n\n\n\n<p>Server render your element and hide\/show it with a class (and appropriate CSS ruleset) like <code>el.classList.add('show')<\/code> or <code>el.style.display = 'block'<\/code> instead of creating and inserting the element dynamically with JavaScript. The mostly static DOM is much more performant due to the lack of garbage collection calls and complex client logic.<\/p>\n\n\n\n<p>Don&#8217;t create DOM nodes on the client dynamically if you can avoid it. <\/p>\n\n\n\n<p>But do remember assistive technology. If you want an element both visually hidden and hidden to assistive technology, <code>display: none;<\/code> should do it. But if you want to hide an element and keep it there for assistive technology, look at <a href=\"https:\/\/www.a11yproject.com\/posts\/how-to-hide-content\/\">other methods for hiding content<\/a>. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Prefer <code>textContent<\/code> over <code>innerText<\/code> for reading the content of an element<\/h3>\n\n\n\n<p>The <code>innerText<\/code> method is cool because it is aware of the current styles of an element. It knows if an element is hidden or not, and only gets text if something is actually displaying. The issue with it is that this process of checking styles forces reflow, and is slower.<\/p>\n\n\n\n<p>Reading content with <a href=\"https:\/\/www.measurethat.net\/Benchmarks\/Show\/3618\/0\/createtextnode-vs-textcontent-vs-innertext-vs-innerhtml\"><code>element.textContent<\/code> is much faster than <code>element.innerText<\/code><\/a>, so prefer <code>textContent<\/code> for reading content of an element where possible.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Use <code>insertAdjacentHTML<\/code> over <code>innerHTML<\/code><\/h3>\n\n\n\n<p>The <a href=\"https:\/\/www.measurethat.net\/Benchmarks\/Show\/10750\/0\/insertadjacenthtml-vs-innerhtml#latest_results_block\"><code>insertAdjacentHTML<\/code> method is much faster than <code>innerHTML<\/code><\/a> because it doesn&#8217;t have to destroy the DOM first before inserting. <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Element\/insertAdjacentHTML\">The method<\/a> is flexible in where it places the new HTML, for example:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">el.insertAdjacentHTML(<span class=\"hljs-string\">\"afterbegin\"<\/span>, html);\nel.insertAdjacentHTML(<span class=\"hljs-string\">\"beforeend\"<\/span>, html);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">The Fastest Approach is to use <code>insertAdjacentElement<\/code> or <code>appendChild<\/code><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Approach #1: Use the <code>template<\/code> tag to create HTML templates and <code>appendChild<\/code> to insert new HTML<\/h3>\n\n\n\n<p>These are the fastest methods are to append fully formed DOM elements. An established pattern for this is to create an HTML template with the <code>&lt;template&gt;<\/code> tag to create the elements, then insert them into the DOM with <code>insertAdjacentElement<\/code> or <code>appendChild<\/code> methods.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"card_template\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">article<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"card__body\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">'card__body__image'<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">'card__body__content'<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">article<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createCardElement<\/span>(<span class=\"hljs-params\">title, body<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> template = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'card_template'<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> element = template.content.cloneNode(<span class=\"hljs-literal\">true<\/span>).firstElementChild;\n  <span class=\"hljs-keyword\">const<\/span> &#91;cardTitle] = element.getElementsByTagName(<span class=\"hljs-string\">\"h3\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;cardBody] = element.getElementsByTagName(<span class=\"hljs-string\">\"section\"<\/span>);\n  &#91;cardTitle.textContent, cardBody.textContent] = &#91;title, body];\n  <span class=\"hljs-keyword\">return<\/span> element;\n}\n\ncontainer.appendChild(createCardElement(\n  <span class=\"hljs-string\">\"Frontend System Design: Fundamentals\"<\/span>,\n  <span class=\"hljs-string\">\"This is a random content\"<\/span>\n))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can see this in action in the new, <a href=\"https:\/\/frontendmasters.com\/courses\/frontend-system-design\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">Front-End System Design course<\/a>, where Evgenni builds an infinite scrolling social news feed from scratch!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Approach #2: Use <code>createDocumentFragment<\/code> with <code>appendChild<\/code> to Batch Inserts<\/h3>\n\n\n\n<p><code>DocumentFragment<\/code> is a lightweight, &#8220;empty&#8221; document object that can hold DOM nodes. It&#8217;s not part of the active DOM tree, making it ideal for preparing <em>multiple<\/em> elements for insertion.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> fragment = <span class=\"hljs-built_in\">document<\/span>.createDocumentFragment();\n<span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">let<\/span> i = <span class=\"hljs-number\">0<\/span>; i &lt; <span class=\"hljs-number\">1000<\/span>; i++) {\n  <span class=\"hljs-keyword\">const<\/span> li = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'li'<\/span>);\n  li.textContent = <span class=\"hljs-string\">`Item <span class=\"hljs-subst\">${i}<\/span>`<\/span>;\n  fragment.appendChild(li);\n}\n<span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'myList'<\/span>).appendChild(fragment);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This approach minimizes reflows and repaints by inserting all elements at once, rather than individually.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Manage References When Nodes are Removed<\/h2>\n\n\n\n<p>When you remove a DOM node, you don&#8217;t want references sitting around that prevent the garbage collector from cleaning up associated data. We can use <code>WeakMap<\/code> and <code>WeakRef<\/code> to avoid leaky references.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Associate data to DOM nodes with <code>WeakMap<\/code><\/h3>\n\n\n\n<p>You can associate data to DOM nodes using <code>WeakMap<\/code>. That way if the DOM node is removed later, the reference to the data will be gone for good.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> DOMdata = { <span class=\"hljs-string\">'logo'<\/span>: <span class=\"hljs-string\">'Frontend Masters'<\/span> };\n<span class=\"hljs-keyword\">let<\/span> DOMmap = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">WeakMap<\/span>();\n<span class=\"hljs-keyword\">let<\/span> el = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\".FmLogo\"<\/span>);\nDOMmap.set(el, DOMdata);\n<span class=\"hljs-built_in\">console<\/span>.log(DOMmap.get(el)); <span class=\"hljs-comment\">\/\/ { 'logo': 'Frontend Masters' }<\/span>\nel.remove(); <span class=\"hljs-comment\">\/\/ DOMdata is able to be garbage collected<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Using weak maps ensures references to data doesn&#8217;t stick around if a DOM element is removed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Clean up after Garbage Collection using <code>WeakRef<\/code><\/h3>\n\n\n\n<p>In the following example, we are creating a <code>WeakRef<\/code> to a DOM node:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Counter<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>(element) {\n    <span class=\"hljs-comment\">\/\/ Remember a weak reference to the DOM element<\/span>\n    <span class=\"hljs-keyword\">this<\/span>.ref = <span class=\"hljs-keyword\">new<\/span> WeakRef(element);\n    <span class=\"hljs-keyword\">this<\/span>.start();\n  }\n\n  start() {\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">this<\/span>.timer) {\n      <span class=\"hljs-keyword\">return<\/span>;\n    }\n\n    <span class=\"hljs-keyword\">this<\/span>.count = <span class=\"hljs-number\">0<\/span>;\n\n    <span class=\"hljs-keyword\">const<\/span> tick = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n      <span class=\"hljs-comment\">\/\/ get the element from the weak reference, if it still exists<\/span>\n      <span class=\"hljs-keyword\">const<\/span> element = <span class=\"hljs-keyword\">this<\/span>.ref.deref();\n      <span class=\"hljs-keyword\">if<\/span> (element) {\n        <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Element is still in memory, updating count.\"<\/span>)\n        element.textContent = <span class=\"hljs-string\">`Counter: <span class=\"hljs-subst\">${++<span class=\"hljs-keyword\">this<\/span>.count}<\/span>`<\/span>;\n      } <span class=\"hljs-keyword\">else<\/span> {\n        <span class=\"hljs-comment\">\/\/ The element doesn't exist anymore<\/span>\n        <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Garabage Collector ran and element is GONE \u2013\u00a0clean up interval\"<\/span>);\n        <span class=\"hljs-keyword\">this<\/span>.stop();\n        <span class=\"hljs-keyword\">this<\/span>.ref = <span class=\"hljs-literal\">null<\/span>;\n      }\n    };\n\n    tick();\n    <span class=\"hljs-keyword\">this<\/span>.timer = setInterval(tick, <span class=\"hljs-number\">1000<\/span>);\n  }\n\n  stop() {\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">this<\/span>.timer) {\n      clearInterval(<span class=\"hljs-keyword\">this<\/span>.timer);\n      <span class=\"hljs-keyword\">this<\/span>.timer = <span class=\"hljs-number\">0<\/span>;\n    }\n  }\n}\n\n<span class=\"hljs-keyword\">const<\/span> counter = <span class=\"hljs-keyword\">new<\/span> Counter(<span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"counter\"<\/span>));\nsetTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"counter\"<\/span>).remove();\n}, <span class=\"hljs-number\">5000<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After removing the node, you can watch your console to see when the actual garbage collection happens, or you can force it to happen yourself using the Performance tab in your developer tools:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"637\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/force-gc.jpg?resize=1024%2C637&#038;ssl=1\" alt=\"\" class=\"wp-image-1562\" style=\"width:619px;height:auto\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/force-gc.jpg?resize=1024%2C637&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/force-gc.jpg?resize=300%2C187&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/force-gc.jpg?resize=768%2C478&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/force-gc.jpg?w=1250&amp;ssl=1 1250w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n<\/div>\n\n\n<p>Then you can be sure all references are gone and timers are cleaned up.<\/p>\n\n\n\n<p>Note: Try not to overuse WeakRef\u2014this magic does come at a cost. It&#8217;s better for performance if you can explicitly manage references.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cleaning up Event Listeners<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Manually remove events with <code>removeEventListener<\/code><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handleClick<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">\"Button was clicked!\"<\/span>);\n  el.removeEventListener(<span class=\"hljs-string\">\"click\"<\/span>, handleClick);\n}\n\n<span class=\"hljs-comment\">\/\/ Add an event listener to the button<\/span>\n<span class=\"hljs-keyword\">const<\/span> el = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#button\"<\/span>);\nel.addEventListener(<span class=\"hljs-string\">\"click\"<\/span>, handleClick);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Use the <code>once<\/code> param for one and done events<\/h3>\n\n\n\n<p>This same behavior as above could be achieved with the &#8220;once&#8221; param:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">el.addEventListener(<span class=\"hljs-string\">'click'<\/span>, handleClick, {\n  <span class=\"hljs-attr\">once<\/span>: <span class=\"hljs-literal\">true<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Adding a third parameter to <code>addEventListener<\/code> with a boolean value indicating that the listener should be invoked at most <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/EventTarget\/addEventListener#once\">once<\/a> after being added. The listener is automatically removed when invoked.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Use event delegation to bind fewer events<\/h3>\n\n\n\n<p>If you are building up and replacing nodes frequently in a highly dynamic component, it&#8217;s more expensive to have to setup all their respective event listeners as you&#8217;re building the nodes.<\/p>\n\n\n\n<p>Instead, you can bind an event closer to the root level. Since events bubble up the DOM, you can check the <code>event.target<\/code> (original target of the event) to catch and respond to the event.<\/p>\n\n\n\n<p>Using <code>matches(selector)<\/code> only matches the current element, so it needs to be the leaf node.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> rootEl = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#root\"<\/span>);\n<span class=\"hljs-comment\">\/\/ Listen for clicks on the entire window<\/span>\nrootEl.addEventListener(<span class=\"hljs-string\">'click'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">event<\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ if the element is clicked has class \"target-element\"<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (event.target.matches(<span class=\"hljs-string\">'.target-element'<\/span>)) doSomething();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>More than likely, you&#8217;ll have elements like <code>&lt;div class=\"target-element\"&gt;&lt;p&gt;...&lt;\/p&gt;&lt;\/div&gt;<\/code> in this case you&#8217;d need to use the <code>.closest(element)<\/code> method.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> rootEl = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">\"#root\"<\/span>);\n<span class=\"hljs-comment\">\/\/ Listen for clicks on the entire window<\/span>\nrootEl.addEventListener(<span class=\"hljs-string\">'click'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">event<\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ if the element is clicked has a parent with \"target-element\"<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (event.target.closest(<span class=\"hljs-string\">'.target-element'<\/span>)) doSomething();\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This method allows you to not worry about attaching and removing listeners after dynamically injecting elements.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use AbortController to unbind groups of events<\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> button = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'button'<\/span>);\n<span class=\"hljs-keyword\">const<\/span> controller = <span class=\"hljs-keyword\">new<\/span> AbortController();\n<span class=\"hljs-keyword\">const<\/span> { signal } = controller;\n\nbutton.addEventListener(\n  <span class=\"hljs-string\">'click'<\/span>, \n  () =&gt; <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'clicked!'<\/span>), \n  { signal }\n);\n\n<span class=\"hljs-comment\">\/\/ Remove the listener!<\/span>\ncontroller.abort();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can use the <code>AbortController<\/code> to remove sets of events.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">let<\/span> controller = <span class=\"hljs-keyword\">new<\/span> AbortController();\n<span class=\"hljs-keyword\">const<\/span> { signal } = controller;\n\nbutton.addEventListener(<span class=\"hljs-string\">'click'<\/span>, () =&gt; <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'clicked!'<\/span>), { signal });\n<span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">'resize'<\/span>, () =&gt; <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'resized!'<\/span>), { signal });\n<span class=\"hljs-built_in\">document<\/span>.addEventListener(<span class=\"hljs-string\">'keyup'<\/span>, () =&gt; <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'pressed!'<\/span>), { signal });\n\n<span class=\"hljs-comment\">\/\/ Remove all listeners at once:<\/span>\ncontroller.abort();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Shout out to Alex MacArthur for the <a href=\"https:\/\/www.macarthur.me\/posts\/options-for-removing-event-listeners#using-abortcontroller\">AbortController code example<\/a> on this one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Profiling &amp; Debugging<\/h2>\n\n\n\n<p>Measure your DOM to make sure it is <a href=\"https:\/\/web.dev\/dom-size-and-interactivity\/\">not too large<\/a>. <\/p>\n\n\n\n<p>Here&#8217;s a brief guide on using Chrome DevTools for memory profiling:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open Chrome DevTools<\/li>\n\n\n\n<li>Go to the &#8220;Memory&#8221; tab<\/li>\n\n\n\n<li>Choose &#8220;Heap snapshot&#8221; and click &#8220;Take snapshot&#8221;<\/li>\n\n\n\n<li>Perform your DOM operations<\/li>\n\n\n\n<li>Take another snapshot<\/li>\n\n\n\n<li>Compare snapshots to identify memory growth<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/developer.chrome.com\/docs\/devtools\/memory-problems\/heap-snapshots\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"887\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?resize=1024%2C887&#038;ssl=1\" alt=\"\" class=\"wp-image-3260\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?resize=1024%2C887&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?resize=300%2C260&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?resize=768%2C665&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?resize=1536%2C1330&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/take-heap-snapshot_1920.png?w=1866&amp;ssl=1 1866w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><\/figure>\n\n\n\n<p>Key things to look for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Unexpectedly retained DOM elements<\/li>\n\n\n\n<li>Large arrays or objects that aren&#8217;t being cleaned up<\/li>\n\n\n\n<li>Increasing memory usage over time (potential memory leak)<\/li>\n<\/ul>\n\n\n\n<p>You can also use the &#8220;Performance&#8221; tab to record memory usage over time:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Go to the &#8220;Performance&#8221; tab<\/li>\n\n\n\n<li>Check &#8220;Memory&#8221; in the options<\/li>\n\n\n\n<li>Click &#8220;Record&#8221;<\/li>\n\n\n\n<li>Perform your DOM operations<\/li>\n\n\n\n<li>Stop recording and analyze the memory graph<\/li>\n<\/ol>\n\n\n\n<p>This will help you visualize memory allocation and identify potential leaks or unnecessary allocations during DOM manipulation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">JavaScript Execution Time Analysis<\/h3>\n\n\n\n<p>In addition to memory profiling, the Performance tab in Chrome DevTools is invaluable for analyzing JavaScript execution time, which is crucial when optimizing DOM manipulation code.<\/p>\n\n\n\n<p>Here&#8217;s how to use it:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open Chrome DevTools and go to the &#8220;Performance&#8221; tab<\/li>\n\n\n\n<li>Click the record button<\/li>\n\n\n\n<li>Perform the DOM operations you want to analyze<\/li>\n\n\n\n<li>Stop the recording<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/frontendmasters.com\/courses\/blazingly-fast-js\/inspecting-debugging-performance\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"577\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=1024%2C577&#038;ssl=1\" alt=\"Excerpt from ThePrimeagen's \nInspecting &amp; Debugging Performance lesson\" class=\"wp-image-3262\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=1024%2C577&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=768%2C433&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=1536%2C866&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/07\/Screenshot-2024-07-28-at-9.17.25%E2%80%AFPM.png?resize=2048%2C1155&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><\/figure>\n\n\n\n<p>The resulting timeline will show you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>JavaScript execution (yellow)<\/li>\n\n\n\n<li>Rendering activities (purple)<\/li>\n\n\n\n<li>Painting (green)<\/li>\n<\/ul>\n\n\n\n<p>Look for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Long yellow bars, indicating time-consuming JavaScript operations<\/li>\n\n\n\n<li>Frequent short yellow bars, which might indicate excessive DOM manipulation<\/li>\n<\/ul>\n\n\n\n<p>To dive deeper:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Click on a yellow bar to see the specific function call and its execution time<\/li>\n\n\n\n<li>Look at the &#8220;Bottom-Up&#8221; and &#8220;Call Tree&#8221; tabs to identify which functions are taking the most time<\/li>\n<\/ul>\n\n\n\n<p>This analysis can help you pinpoint exactly where your DOM manipulation code might be causing performance issues, allowing for targeted optimizations.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Debugging Resources<\/h3>\n\n\n\n<p>Articles by the Chrome dev team:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/developer.chrome.com\/docs\/devtools\/memory-problems\">Fixing memory issues<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/developer.chrome.com\/docs\/devtools\/memory-problems\/heap-snapshots\">Chrome Docs on Recording heap snapshots<\/a> <\/li>\n\n\n\n<li><a href=\"https:\/\/developer.chrome.com\/docs\/devtools\/performance\/reference\">Chrome Performance tab reference docs<\/a><\/li>\n<\/ul>\n\n\n\n<p>Courses that cover the memory and performance analysis and Chrome Dev Tools further:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/frontendmasters.com\/courses\/blazingly-fast-js\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">Blazingly Fast JS<\/a> by ThePrimeagen<\/li>\n\n\n\n<li><a href=\"https:\/\/frontendmasters.com\/courses\/web-app-performance\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">Web App Performance<\/a> by Maximiliano Firtman<\/li>\n\n\n\n<li><a href=\"https:\/\/frontendmasters.com\/courses\/react-performance\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">React Performance<\/a> by Steve Kinney<\/li>\n\n\n\n<li><a href=\"https:\/\/frontendmasters.com\/courses\/dev-tools\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">Chrome Dev Tools<\/a> by Jon Kuperman<\/li>\n\n\n\n<li><a href=\"https:\/\/frontendmasters.com\/courses\/javascript-cpu-vm\/?utm_source=boost&amp;utm_medium=blog&amp;utm_campaign=dom-patterns\">Bare Metal JavaScript: The JavaScript Virtual Machine<\/a> by Mi\u0161ko Hevery<\/li>\n<\/ul>\n\n\n\n<p>Remember, efficient DOM manipulation isn&#8217;t just about using the right methods\u2014it&#8217;s also about understanding when and how often you&#8217;re interacting with the DOM. Excessive manipulation, even with efficient methods, can still lead to performance issues.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Key Takeaways for DOM Optimization<\/h2>\n\n\n\n<p>Efficient DOM manipulation knowledge is important when creating performance-sensitive web apps. While modern frameworks offer convenience and abstraction, understanding and applying these low-level techniques can significantly boost your app&#8217;s performance, especially in demanding scenarios.<\/p>\n\n\n\n<p>Here&#8217;s a recap:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Prefer modifying existing elements over creating new ones when possible.<\/li>\n\n\n\n<li>Use efficient methods like <code>textContent<\/code>, <code>insertAdjacentHTML<\/code>, and <code>appendChild<\/code>.<\/li>\n\n\n\n<li>Manage references carefully, leveraging <code>WeakMap<\/code> and <code>WeakRef<\/code> to avoid memory leaks.<\/li>\n\n\n\n<li>Clean up event listeners properly to prevent unnecessary overhead.<\/li>\n\n\n\n<li>Consider techniques like event delegation for more efficient event handling.<\/li>\n\n\n\n<li>Use tools like <code>AbortController<\/code> for easier management of multiple event listeners.<\/li>\n\n\n\n<li>Employ <code>DocumentFragment<\/code>s for batch insertions and understand concepts like the virtual DOM for broader optimization strategies.<\/li>\n<\/ol>\n\n\n\n<p>Remember, the goal isn&#8217;t always to forgo frameworks and manually manipulate the DOM for every project. Rather, it&#8217;s to understand these principles so you can make informed decisions about when to use frameworks and when to optimize at a lower level. Tools like memory profiling and performance benchmarking can guide these decisions.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-todomvc\/\">Writing a TodoMVC App with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-reactivity\/\">Patterns for Reactivity with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/patterns-for-memory-efficient-dom-manipulation\/\">Patterns for Memory Efficient DOM Manipulation with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>JavaScript Frameworks generally do a lot of DOM handling for you, but doing it yourself can be the most performant option, and there are quite a few best practices.<\/p>\n","protected":false},"author":2,"featured_media":1639,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[149,3],"class_list":["post-1551","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-dom","tag-javascript"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/04\/efficiency-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1551","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=1551"}],"version-history":[{"count":41,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1551\/revisions"}],"predecessor-version":[{"id":3298,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/1551\/revisions\/3298"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/1639"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=1551"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=1551"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=1551"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}