1. Show the different ways of selecting an element from DOM
- The element can be selected using its tagname, id, attribute, value etc
document.getElementById("elementId"); // elementId is id of the element
document.querySelector(".my-class"); // element having a class 'my-class'
Notes
Above selectors are used to select a first matching element reference even if multiple matches be there
References
- https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector (opens in a new tab)
- https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById (opens in a new tab)
2. Show the ways to loop over the Nodelist obtained after querying for the elements
- The Nodelist can be obtained by queryng for multiple elements
- As list of elements are array like object (not pure JS array), all the array methods can't be directly used
// Lets assume DOM elements are fetched in the variable domElements
for (let element of domElements) {
// perform operation on element
}
References
3. Design and Implement a Node Store, which supports DOM element as key
- Implement it without using inbuilt Map
- Can you do it in O(1) Time complexity?
class NodeStore {
constructor() {
this.store = {};
}
/**
* @param {Node} node
* @param {any} value
*/
set(node, value) {
node.__nodeIdentifier__ = Symbol();
this.store[node.__nodeIdentifier__] = value;
}
/**
* @param {Node} node
* @return {any}
*/
get(node) {
return this.store[node.__nodeIdentifier__];
}
/**
* @param {Node} node
* @return {Boolean}
*/
has(node) {
return !!this.store[node.__nodeIdentifier__];
}
}
References
4. Implement a function to find the closest ancestor with the provided selector (Element.closest() method)
- The closest() method traverses the Element and its parents (heading toward the document root) until it finds a node that matches the provided selector string. Will return itself or the matching ancestor. If no such element exists, it returns null.
Element.prototype.closest = function (selector) {
var el = this;
while (el) {
if (el.matches(selector)) {
return el;
}
el = el.parentElement;
}
return null;
};
References
5. Write a function to find the corresponding node in two identical DOM trees
- Given two same DOM tree A, B, and an Element a in A, find the corresponding Element b in B. By corresponding, we mean a and b have the same relative position to their DOM tree root.
const A = document.createElement("div");
A.innerHTML = `
<div>
<div>
<div>
<div id="node1"></div>
</div>
<div>
</div>
<div>
<div>
<p id="node2"></p>
</div>
</div>
<div>
</div>`;
const B = A.cloneNode(true);
const node1 = A.querySelector("#node1");
const node2 = A.querySelector("#node2");
const node1Target = B.querySelector("#node1");
const node2Target = B.querySelector("#node2");
findCorrespondingNode(A, B, node1); // node1Target
findCorrespondingNode(A, B, node2); // node2Target
const findCorrespondingNode = (rootA, rootB, target) => {
if (rootA === target) return rootB;
if (rootA.childElementCount) {
for (let i = 0; i < rootA.childElementCount; i++) {
let result = findCorrespondingNode(
rootA.children[i],
rootB.children[i],
target
);
if (result) {
return result;
}
}
}
};
References
6. Write a function to get depth of a given DOM tree
- A depth of a given DOM tree is the max depth till which DOM nodes are nested
/**
* @param {HTMLElement | null} tree
* @return {number}
*/
function getHeight(root) {
if (!root) return 0;
let maxDepth = 0;
const helper = (current, depth = 1) => {
if (current.hasChildNodes()) {
for (let child of current.children) {
helper(child, depth + 1);
}
}
maxDepth = Math.max(maxDepth, depth);
};
helper(root);
return maxDepth;
}
7. Implement a function to get the root node of a given DOM fragment (document.getRootNode() method)
- Root node is the topmost parent node of any given DOM fragment
/**
* @param {HTMLElement | null} tree
* @return {HTMLElement | null}
*/
function getRootNode(tree) {
if (!tree) return null;
while (tree.parentElement) {
tree = tree.parentElement;
}
return tree;
}
References
8. Implement a function to get unique tag names in a given DOM tree
/**
* @param {HTMLElement | null} tree
* @return {Array}
*/
function getUniqueTags(root, result = new Set()) {
if (!root) return [];
if (!result.has(root.tagName)) {
result.add(root.tagName);
}
if (root.hasChildNodes()) {
for (let child of root.children) {
getUniqueTags(child, result);
}
}
return [...result];
}
References
9. Implement a function to get elements by tag name (document.getElementsByTagName() method)
- The getElementsByTagName method of Document interface returns an HTMLCollection of elements with the given tag name.
- For example, document.getElementsByTagName('div') returns a collection of all div elements in the document.
/**
* @param {HTMLElement | null} tree
* @return {Array}
*/
function getElementsByTagName(root, tagName) {
if (!root) return [];
let result = [];
if (root.tagName.toLowerCase() === tagName.toLowerCase()) {
result.push(root);
}
if (root.hasChildNodes()) {
for (let child of root.children) {
result = result.concat(getElementsByTagName(child, tagName));
}
}
return result;
}
References
10. Implement a function to check if a given DOM tree has duplicate IDs
- In a given DOM tree, the id on each node has be unique
- Although HTML is very forgiving, but we should avoid duplicate identifiers
/**
* @param {HTMLElement | null} tree
* @return {Boolean}
*/
function hasDuplicateId(tree, idSet = new Set()) {
if (!tree) return false;
if (idSet.has(tree.id)) return true;
tree.id && idSet.add(tree.id);
if (tree.hasChildNodes()) {
for (let child of tree.children) {
const result = hasDuplicateId(child, idSet);
if (result) return true;
}
}
return false;
}
11. Implement a function to get all descendants of a given DOM node
- This function retrieves all child elements, regardless of depth.
/**
* @param {HTMLElement | null} tree
* @return {Array}
*/
function getAllDescendants(root) {
if (!root) return [];
let descendants = [];
const helper = (node) => {
if (node) {
descendants.push(node);
if (node.children.length) {
for (let child of node.children) {
helper(child);
}
}
}
};
helper(root);
return descendants;
}
References
12. Implement a function to clone a DOM tree
- This function creates a deep copy of the given DOM node and its descendants.
/**
* @param {HTMLElement | null} tree
* @return {HTMLElement | null}
*/
function cloneDOMTree(root) {
return root ? root.cloneNode(true) : null;
}
References
13. Implement a function to count all elements in a DOM tree
- This function counts all child elements, regardless of depth.
/**
* Counts all child elements in a DOM tree.
*
* @param {HTMLElement | null} root - The root element from which to start counting.
* @return {number} - The total number of child elements.
*/
function countElements(root) {
if (!root) return 0;
let count = 1;
for (let child of root.children) {
count += countElements(child);
}
return count;
}
14. Implement a function to serialize a DOM tree into a string
- This function converts a DOM tree into a string representation.
/**
* @param {HTMLElement | null} tree
* @return {string}
*/
function serializeDOM(root) {
if (!root) return '';
let str = `<${root.tagName.toLowerCase()}`;
for (let attr of root.attributes) {
str += ` ${attr.name}="${attr.value}"`;
}
str += '>';
if (root.hasChildNodes()) {
for (let child of root.children) {
str += serializeDOM(child);
}
}
str += `</${root.tagName.toLowerCase()}>`;
return str;
}
References
15. What is event delegation in the DOM and how can it be implemented in JavaScript?
- Event delegation allows you to attach a single event listener to a parent element to manage events from its child elements, leveraging event bubbling. This improves performance and simplifies handling dynamically added elements.
Here’s an example of how event delegation works:
document.querySelector('#parent').addEventListener('click', function(event) {
if (event.target && event.target.matches('button')) {
console.log('Button clicked:', event.target.textContent);
}
});
This approach optimizes performance and reduces memory usage, as fewer event listeners are attached to individual elements.
References
16. How can you find all leaf nodes in a given DOM tree?
- Leaf nodes in a DOM tree are elements that do not have any child elements. You can traverse the tree recursively to collect all leaf nodes.
Implementation:
function getLeafNodes(element) {
let leafNodes = [];
// Base case: if the element has no children, it's a leaf node
if (!element.children || element.children.length === 0) {
leafNodes.push(element);
return leafNodes;
}
// Recursive case: traverse through each child
for (let i = 0; i < element.children.length; i++) {
leafNodes = leafNodes.concat(getLeafNodes(element.children[i]));
}
return leafNodes;
}
// Example usage:
const leafNodes = getLeafNodes(document.body);
console.log('Leaf nodes:', leafNodes);
In this implementation:
The function checks if the current element has no children. If so, it adds it to the leafNodes array. Otherwise, it recursively collects leaf nodes from each child.
References