Guide on generating an array containing only leaf nodes within an HTML DOM utilizing JavaScript

My query is about how to create a JavaScript function that returns an array of all the leaf nodes present in an HTML document. I have to manipulate this list further. For instance, if the HTML looks like:

<body>
   <div>
      <div>
         <p id="para1"></p>
      </div>
   </div>
   <p id="para2"></p>
</body>

then the goal is to generate an array with the two nodes having the ids para1 and para2.

Note: The requirement is to get the nodes themselves, not just their ids. However, extracting nodes from ids can be done, so it's not a major concern.

Answer №1

Below is a straightforward function that retrieves leaf nodes by considering all nodes, including text nodes (ensuring elements containing text nodes are not returned):

function getLeafNodes(master) {
    var nodes = Array.prototype.slice.call(master.getElementsByTagName("*"), 0);
    var leafNodes = nodes.filter(function(elem) {
        return !elem.hasChildNodes();
    });
    return leafNodes;
}

View the working demo here.

Note: The use of the .filter() method requires IE9 compatibility. To support earlier versions of IE, consider implementing a polyfill for .filter() or opt for manual array iteration.


If excluding text nodes and focusing solely on leaf elements, utilize the following version:

function getLeafNodes(master) {
    var nodes = Array.prototype.slice.call(master.getElementsByTagName("*"), 0);
    var leafNodes = nodes.filter(function(elem) {
        if (elem.hasChildNodes()) {
            // Check for child elements
            for (var i = 0; i < elem.childNodes.length; i++) {
                if (elem.childNodes[i].nodeType == 1) {
                    // Child element exists - exclude parent element
                    return false;
                }
            }
        }
        return true;
    });
    return leafNodes;
}

Working demo available here.


For a recursive solution disregarding text nodes, refer to the snippet below:

function getLeafNodes(master) {
    var results = [];
    var children = master.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].nodeType == 1) {
            var childLeafs = getLeafNodes(children[i]);
            if (childLeafs.length) {
                // Concatenate child leaf nodes with current results
                results = results.concat(childLeafs);
            } else {
                // Current node is a leaf
                results.push(children[i]);
            }
        }
    }
    // If no leaves found at this level, treat current node as leaf
    if (!results.length) {
        results.push(master);
    }
    return results;
}

See it in action here.

Answer №2

When referring to a "leaf node," it typically means an element in the tree structure that does not have any children nodes branching off from it. To identify these leaf nodes, you can search through all elements on the page and select those without any children. Here's a simple way to achieve this:

const leafNodes = [];
const elements = document.body.getElementsByTagName("*");
for (let i = 0; i < elements.length; i++) {
    if (elements[i].children.length === 0) {
        leafNodes.push(elements[i]);
    }
}

Answer №3

Would this approach be effective?

let paragraphElements = Array.from(document.getElementsByTagName("p"));

It's worth noting that document.getElementsByTagName("p") already returns an array of elements.

Answer №4

Are you curious about how to populate an array with nodes and then return the array? The solution is to always ensure that the function returns an array and then merge them together:

function generateNodes(element) {
    if (element.childNodes.length === 0) {
        return [element];
    }
    var nodes = [];
    for (var i = 0, l = element.children.length; i < l; i++) {
        nodes.push.apply(nodes, generateNodes(element.children[i]));
    }
    return nodes;
}

Check out the DEMO

Alternatively, you can include an array as an argument and when the element is a leaf node, it will add itself to the array:

function generateLeaves(element, arr) {
    // If no array is passed, create one. This occurs in the initial call.
    arr = arr || [];
    if (element.children.length === 0) {
        arr.push(element);
    }
    var leaves = [];
    for (var i = 0, l = element.children.length; i < l; i++) {
        generateLeaves(element.children[i], arr);
    }
    return arr;
}

View the DEMO here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Removing the "<!doctype html>" tag from a document using cheerio.js is a simple process that involves

Is there a way to remove and <?xml ...> from an HTML document that has been parsed by cherio.js? ?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-tran ...

Here's a unique version: "Discover the process of implementing click functionality, similar to ng-click, on a circle SVG

Within my JS file, I have some HTML text that is being returned to an HTML page. This HTML text contains a circle SVG element, which displays properly when retrieved from the API. When clicking on the circle, I want to call a JavaScript function in the sam ...

Tips for Avoiding "TypeError: fetch failed" on Next.js Page Server Component While Retrieving Data from Spring Boot API

Working on a Next.js application has been quite challenging as I fetch data from my Spring Boot API within a Page server component. Unfortunately, my GitHub Action CI/CD pipeline has been encountering failures during the Docker image build process, specifi ...

Iview Table UI Cell

Is there a way to retrieve the cell data from a library iview table in Vue.js upon clicking? I am looking to capture both the value of the cell and the title of the column, and then modify the CSS of that particular cell. For instance, clicking on one ce ...

Deactivating the submit button after a single click without compromising the form's functionality

In the past, I have posed this question without receiving a satisfactory solution. On my quiz website, I have four radio buttons for answer options. Upon clicking the submit button, a PHP script determines if the answer is accurate. My goal is to disable t ...

Getting the return value from a confirm box in ASP.NET C#

Despite reading through numerous similar questions and answers, I am still unable to find a solution to my problem. I am working on a form that allows users to select a file and choose a date for importing the file. If the selected date is before the last ...

Access to Web Share API requires permission

I am currently attempting to integrate the Web Share API feature into my testing web application, but unfortunately, I seem to be encountering some difficulties. Below is the code snippet I have been working with: const newVariable: any = navigator; {newV ...

Utilize the click spinner feature on a single button in Angular

I have a table with a list of items and each item has a button to request a PDF document from the server. I want to show a spinner while waiting for the document to be received. Currently, when I click on a button, the spinner appears on all buttons instea ...

Combining Multiple If Statements in PHP

Hey all, need some help with a code conundrum I have this lengthy code that tracks user hug counts and provides them with quotes as rewards at irregular intervals. if ($hugs > 0) { $quote = $quotes[0]; } if ($hugs > 5) { $quote = $quotes[1] ...

What is the best way to input keys into the currently selected element?

During my experimentation, I discovered that several modals and dropdowns in my tests open with their input boxes automatically focused. I found a way to verify if an element is in focus, but I'm wondering if there's a quicker method to input ke ...

Error encountered while trying to access the user information from Firestore/Firebase

When I attempt to display the current user that is logged in console.log(firebase.auth().currentUser.uid) The user's UID is outputted. I then decided to retrieve the current user's information from Firestore using the following code: import ...

When the window is loaded, a function is triggered

I attempted to create a function that generates a canvas with customizable width and height parameters. When I tried to call the function with createCanvas(200, 200) in another file, an error appeared on the console: Uncaught ReferenceError: createCan ...

Issues with implementing Dark mode in TailwindCSS with Nuxt.js

After spending a couple of days on this, I'm still struggling to get the dark mode working with Tailwind CSS in Nuxt.js. It seems like there might be an issue with the CSS setup rather than the TypeScript side, especially since I have a toggle that sw ...

Guide to integrating an interactive map with Symfony: linking a popup marker to a template

I am currently working on creating an interactive map to showcase climbing sites in France. The goal is to have a popup marker that directs users to a detailed template of the site when clicked. My project is based on Symfony framework and I've been t ...

Cannot retrieve top 2 values from an object - Angular 6 bug fixing issue

I've been attempting to identify the top 2 values in an object within an array, but I'm encountering issues. Previously, I successfully achieved this with average values that were not within an array, however, when looping through some results an ...

What is the best way to display a collection of data similar to an array? I only want to display the number of characters, not the entire value

I am trying to calculate the number of letters in a given text and print it out in the format 'letter:6' for example if there are 6 letters, the output should be 'letter:6'. However, instead of correctly counting and printing the number ...

Navigate to a specific URL path and send properties as arguments in the function for handling events

I am working on a vuetify autocomplete search feature. When a user selects an item, I need to navigate to a specific route and pass some props along. However, my attempts to change the current route without passing props have resulted in errors. Here is w ...

Implementing X.PagedList within a modal pop-up window

I have implemented a modal pop-up on a webpage: ... <div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="companySearchModal" aria-hidden="true" id="companySearchModal"> <div class="modal-dialog" role="document"> ...

When attempting to pipe a request from Express.js to a PHP built-in server on localhost, the result is an ECONN

I am currently in the process of developing a basic website using npm, which is being hosted by a provider that supports php. The only aspect of the site that requires php functionality is a contact form for sending emails. The rest of the content consist ...

Halting manual scrolling and directing the scroll to a specific position

How can I implement a feature on my website that automatically scrolls to a specific point when the user tries to scroll (mousewheel, touch event, or cursor keys) instead? I'm not sure what this functionality is called, but you can see it in action o ...