Is there a way to compare the equality of two NodeLists?

Suppose I have created a custom function that should return a NodeList:

getNodeList('foo');

I anticipate that this NodeList will match the NodeList returned from:

document.querySelectorAll('.foo');

Is there a way to verify if my assumptions are accurate?

The following comparison method does not yield the desired result:

getNodeList('foo') == document.querySelectorAll('.foo')

It seems like there must be a technical reason why this doesn't work, as evidenced by

document.querySelectorAll('.foo') == document.querySelectorAll('.foo')
, which also fails. This outcome is expected.

How can I determine whether two NodeLists contain identical HTML nodes?

Answer №1

When it comes to comparing arrays, remember that equality is determined by reference, not content.

let a = [1, 2, 3], b = [1, 2, 3]
let c = a
a == c // => true, since both point to `a`
a == b // => false

If you need to compare two array-like objects, you must compare them element by element.

function eq(A, B) {
  if (A.length !== B.length) return false;
  for (let i = 0; i < A.length; i++) {
    if (A[i] !== B[i]) return false;
  }
  return true;
}

You can also utilize some functional programming techniques:

let arrayEq = (A, B) => A.length === B.length && A.every((element, index) => element === B[index]);

Just keep in mind that this approach only works with actual arrays, and not with NodeList objects.


Give it a shot with

eq(getNodeList('foo'), document.querySelectorAll('.foo'))

or

arrayEq(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo'))

Answer №2

Your progress is on the right track, but there are opportunities for improvement in terms of efficiency (avoiding unnecessary recomputations of `document.querySelectorAll(...)` and `indexOf` multiple times).

A critical bug to take note of is that if the return from `querySelectorAll` contains more elements than the first node list, but they are identical, your function will incorrectly output `true`.

To streamline the comparisons even further, consider this revised approach:

function compareNodeLists( nodeList1, nodeList2 ) {
    if ( nodeList1.length !== nodeList2.length ) {
        return false;
    }
    return Array.from( nodeList1 ).every( ( node, index ) => node === nodeList2[ index ] );
}

Answer №3

For those who are open to using a third-party library, consider downloading deep-equal from NPM and executing the following:

deepEqual(Array.from(getNodeList('foo')), Array.from(document.querySelectorAll('.foo')))

By doing this, you are ensuring that your lists are only processed once and all the complexities of list comparison are handled by a separate function. Your code can simply call an equality function without having to deal with the intricacies of navigating through list structures. (But chances are, you already knew that!)

If you find Array.from too verbose, you can use splats instead:

deepEqual([...getNodeList('foo')], [...document.querySelectorAll('.foo')])

For those concerned about efficiency, it might be worth conducting some profiling.

Answer №4

I have devised a solution using ES6 features that appears to be effective:

const isEqual = [...getElements('foo')].every((element, index) => {
    return Array.from(document.querySelectorAll('.foo')).indexOf(element) === index
});

In simple terms, this method checks if each element in the first NodeList can be found in the second NodeList at the same position. If there are any inconsistencies between the NodeLists, the function will return false.

Answer №5

Upon revisiting this code, I found a use case where I needed to compare NodeLists that might not have the same node order but are otherwise identical. Here is the solution I came up with:

function determineEquality(nodeList1, nodeList2) {
    if ((nodeList1.length || Object.keys(nodeList1).length) !== (nodeList2.length || Object.keys(nodeList2).length)) {
        return false;
    }

    return Array.from(nodeList1).every(node1 => {
        return Array.from(nodeList2).some(node2 => node1 === node2)
    });
}

Answer №6

Here's a streamlined and potentially quicker version by @mauroc8:


     const compareNodes = (arrayA, arrayB) => {
        arrayA = Array.from(arrayA)
        arrayB = Array.from(arrayB)
        if (arrayA.length !== arrayB.length) return false

        return arrayA.every((a, b) => a.innerHTML === arrayB[b].innerHTML)
      }

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

Ajax/PHP - unrecognized value in autofill

I have implemented this particular example to execute an ajax data query from a MySQL database, which returns a table. Currently, this setup functions correctly when text is manually entered into the input form. Example: https://i.sstatic.net/TTgLz.jpg T ...

Can the Vue instance be accessed in Axios interceptors?

I've integrated vue-reactive-storage to have a reactive alternative to localStorage in my Vue application. This plugin creates a vue object that is accessible to all components. I'm facing an issue when trying to access this object within my axio ...

Using D3-GraphViz in Javascript along with an Angular template: A step-by-step guide

I am attempting to integrate d3-graphviz following the guidance provided here within an angular template, like in this example. The tutorial on the d3-graphviz website advises me to include the following code in the index.html file: <!DOCTYPE html> & ...

Update the value of a Vue tree select component using JavaScript

I'm working on a school project using plain JavaScript and needed a tree view select with multiple layers. After extensive searching, I stumbled upon this tool. It's working smoothly, but the one thing that has me stumped is how to change its va ...

The outputs of base64(sha256(data)) from nodejs crypto and CryptoJS are showing discrepancies

In my setup, I have a node server and a react-native app. The node server utilizes the crypto module for all cryptographic operations, while the react-native app relies on the crypto-js library due to the unavailability of crypto. You can find a sample co ...

Issue with AngularJS Cross-Origin Resource Sharing (CORS) when making HTTP requests, whereas standard Ajax and jQuery are

I'm currently dealing with a straightforward cross-domain service that is set up to handle Simple CORS requests. When I try to access it using a plain xmlHTTP call or jQuery($.ajax), everything works smoothly. However, when attempting to make the call ...

Failing to retain hyperlinks with ajax

Currently, I am utilizing ajax to transmit data from a sophisticated custom field wysiwyg editor. Within this setup, the specific div with the class 'bio' is what I'm addressing. The issue arises when the data is retrieved - all the original ...

"Utilize browser detection to direct users to appropriate pages - whether by using htaccess or another method

I'm in the process of creating a website and I've noticed that it looks quite bad on Firefox and IE. I was wondering if you could help me figure out how to redirect users to different pages based on the browser they are using. Since I am not an ...

Error: Undefined variable in JavaScript obfuscation

My website has several small JavaScript files that I currently merge into one file and then minimize using terser. The website functions perfectly with the minimized version. Now, I want to take it a step further and obfuscate the code using JavaScript-Ob ...

Using a javascript parameter in a cshtml file to filter data in a datatable

Here is the model code public class viewCase { public List<string> lstCategory { get; set; } public DataTable dtWrkTsk { get; set; } } This is the controller code string query = "SELECT WorkFlowID,Subject,Category FROM CMSTasksWorkFlow" ob ...

Running a server-side function on the client-side in Node.js

I am currently working with the following code snippet on the server: var game = io.listen(app); game.sockets.on('connection', function(socket){ storePlayers(socket.id); //Only the player who connects receives this message socket.em ...

Using a try-catch block within an axios call inside the then method in a Node

when I make the API call below, I encounter errors that I can't seem to handle properly. (node:5548) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Can't set headers after they are sent. (node:5548) [DEP00 ...

Error Timeout Encountered by Langchain UnstructuredDirectoryLoader

I am facing an issue while trying to load a complex PDF file with tables and figures, spanning approximately 600 pages. When utilizing the fast option in Langchain-JS with NextJS Unstructured API, it partially works but misses out on some crucial data. On ...

How can I reset the DefaultValue of my Autocomplete input field after submitting?

Is there a way to reset the default value of my <TextField> inside Autocomplete after form submission? Even after submitting the form, the state of formValues remains as the default value. What can I do to fix this issue? I've attempted to mod ...

Trouble with updating the view when an array is modified in ng-repeat? It seems like using $scope.$apply() may not

When updating the array inside a function, the view does not automatically update. However, if you use console.log to check the array after pushing values, it shows the updated array. Even trying $scope.apply() inside $timeout did not solve this issue. Ja ...

Is there a way to execute this process twice without duplicating the code and manually adjusting the variables?

I'm looking to modify this code so that it can be used for multiple calendars simultaneously. For example, I want something similar to the following: Here is what I currently have: This is my JavaScript code: var Calendar = { start: function () ...

Stop Code Execution || Lock Screen

Is there a way to address the "challenge" I'm facing? I'm an avid gamer who enjoys customizing my game using JavaScript/jQuery with Greasemonkey/Firefox. There are numerous scripts that alter the DOM and input values. In my custom script, I hav ...

Issue with conflicting trigger events for clicking and watching sequences in input text boxes and checkboxes within an AngularJS application

When creating a watch on Text box and Check box models to call a custom-defined function, I want to avoid calling the function during the initial loading of data. To achieve this, I am using a 'needwatch' flag inside the watch to determine when t ...

How to utilize the index in a foreach loop as a variable in JavaScript

I am facing an issue with using the index of a forEach loop to access an array index in my code. It seems to work when I use a number directly, but not when I try to use the index variable. Any suggestions or insights on how to resolve this? let post ...

Angular Tip: Display data in a dropdown using a select element

When I have two select elements and want to display values if they exist: page.ts import { Component } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app ...