Have you ever had the problem of finding the parent of a DOM node in JavaScript, but aren’t sure how many levels you have to traverse up to get to it? Let’s look at this HTML for instance: Show
That’s pretty straightforward, right? Say you want to get the value of 8 after a user clicks the button:
In this very case, the Node.parentNode API is sufficient. What it does is return the parent node of a given element. In the above example, 9is the button clicked; its parent node is the div with the data attribute.But what if the HTML structure is nested deeper than that? It could even be dynamic, depending on its content.
Our job just got considerably more difficult by adding a few more HTML elements. Sure, we could do something like 0, but come on… that isn’t elegant, reusable or scalable.The old way: Using a <div data-id="123"> <article> <header> <h1>Some title</h1> <button>Click me</button> </header> <!-- ... --> </article> </div>1-loopOne solution would be to make use of a 1 loop that runs until the parent node has been found.
Using the same HTML example from above again, it would look like this:
This solution is far from perfect. Imagine if you want to use IDs or classes or any other type of selector, instead of the tag name. At least it allows for a variable number of child nodes between the parent and our source. There’s also jQueryBack in the day, if you didn’t wanted to deal with writing the sort of function we did above for each application (and let’s be real, who wants that?), then a library like jQuery came in handy (and it still does). It offers a 3 method for exactly that:
The new way: Using <div data-id="123"> <article> <header> <h1>Some title</h1> <button>Click me</button> </header> <!-- ... --> </article> </div>4Even though jQuery is still a valid approach (hey, some of us are beholden to it), adding it to a project only for this one method is overkill, especially if you can have the same with native JavaScript. And that’s where 5 comes into action:
There we go! That’s how easy it can be, and without any libraries or extra code. 4 allows us to traverse up the DOM until we get an element that matches the given selector. The awesomeness is that we can pass any selector we would also give to 7 or 8. It can be an ID, class, data attribute, tag, or whatever.
If 5 finds the parent node based on the given selector, it returns it the same way as 0. Otherwise, if it doesn’t find a parent, it returns 1 instead, making it easy to use with 2 conditions:
Ready for a few real-life examples? Let’s go! Use Case 1: DropdownsCodePen Embed Fallback Our first demo is a basic (and far from perfect) implementation of a dropdown menu that opens after clicking one of the top-level menu items. Notice how the menu stays open even when clicking anywhere inside the dropdown or selecting text? But click somewhere on the outside, and it closes. The 5 API is what detects that outside click. The dropdown itself is a 4 element with a 5 class, so clicking anywhere outside the menu will close it. That’s because the value for 6 is going to be 1 since there is no parent node with this class.
Inside the 8 callback function, a condition decides what to do: close the dropdown. If somewhere else inside the unordered list is clicked, 5 will find and return it, causing the dropdown to stay open.Use Case 2: TablesCodePen Embed Fallback This second example renders a table that displays user information, let’s say as a component in a dashboard. Each user has an ID, but instead of showing it, we save it as a data attribute for each 0 element. 0The last column contains two buttons for editing and deleting a user from the table. The first button has a 1 attribute of 2, and the second button is 3. When we click on either of them, we want to trigger some action (like sending a request to a server), but for that, the user ID is needed.A click event listener is attached to the global window object, so whenever the user clicks somewhere on the page, the callback function 8 is called. 1If a click happens somewhere else other than one of these buttons, no 1 attribute exists, hence nothing happens. However, when clicking on either button, the action will be determined (that’s called event delegation by the way), and as the next step, the user ID will be retrieved by calling 6: 2This function expects a DOM node as the only parameter and, when called, uses 5 to find the table row that contains the pressed button. It then returns the 8 value, which can now be used to send a request to a server.Use Case 3: Tables in ReactLet’s stick with the table example and see how we’d handle it on a React project. Here’s the code for a component that returns a table: 3I find that this use case comes up frequently — it’s fairly common to map over a set of data and display it in a list or table, then allow the user to do something with it. Many people use inline arrow-functions, like so: 4While this is also a valid way of solving the issue, I prefer to use the 8 technique. One of the drawbacks of the inline arrow-function is that each time React re-renders the list, it needs to create the callback function again, resulting in a possible performance issue when dealing with large amounts of data.In the callback function, we simply deal with the event by extracting the target (the button) and getting the parent 0 element that contains the 8 value. 5Use Case 4: ModalsCodePen Embed Fallback This last example is another component I’m sure you’ve all encountered at some point: a modal. Modals are often challenging to implement since they need to provide a lot of features while being accessible and (ideally) good looking. We want to focus on how to close the modal. In this example, that’s possible by either pressing 2 on a keyboard, clicking on a button in the modal, or clicking anywhere outside the modal.In our JavaScript, we want to listen for clicks somewhere in the modal: 6The modal is hidden by default through a 3 utility class. It’s only when a user clicks the big red button that the modal opens by removing this class. And once the modal is open, clicking anywhere inside it — with the exception of the close button — will not inadvertently close it. The event listener callback function is responsible for that: 7 9 is the DOM node that’s clicked which, in this example, is the entire backdrop behind the modal, 5. This DOM node is not within 6, hence 4 can bubble up all it wants and won’t find it. The condition checks for that and triggers the 8 function.Clicking somewhere inside the nodal, say the heading, would make 6 the parent node. In that case, the condition isn’t truthy, leaving the modal in its open state.Oh, and about browser support…As with any cool “new” JavaScript API, browser support is something to consider. The good news is that 5 is not that new and is supported in all of the major browsers for quite some time, with a whopping 94% support coverage. I’d say this qualifies as safe to use in a production environment.The only browser not offering any support whatsoever is Internet Explorer (all versions). If you have to support IE, then you might be better off with the jQuery approach. As you can see, there are some pretty solid use cases for 5. What libraries, like jQuery, made relatively easy for us in the past can now be used natively with vanilla JavaScript.Thanks to the good browser support and easy-to-use API, I heavily depend on this little method in many applications and haven’t been disappointed, yet. |