Find the closest interactive element (button, input, link) to a given DOM node.
To find closest element we can use element.closest so it will be something like
const closestInteractiveEl = (node) => {
return node.closest('button, a, input');
}
But this will only return el if current node or parent nodes are interactive. Because closest traverse till root from current element and returns if selector matches.
According to question we need to find closest interactive element. This can be sibling, children or ancestor.
We can solve this by getting all interactive nodes and their distance from current node. We will return element who has minimum distance from current node. If we have to get distance by traversing DOM this will be a complex problem as calculating distance between two nodes is time consuming task. Instead we will find visual proximity, which means that we will find distance between current and interactive node when they are rendered on the screen.
const findClosestIinteractiveElement = ()=> {
const interactiveSelectors = ['button', 'a', 'input', '[role="button"]', '[role="link"]','[role="checkbox"]',
'[role="radio"]'];
const interactiveElements = Array.from(document.querySelectorAll(interactiveSelectors.join(',')))
}
if(interactiveElements.length===0){
return {element: null, distance: Infinity};
}
// Get the position of the reference node
const nodeRect = node.getBoundingClientRect();
const nodeCenter = {
x: nodeRect.left + nodeRect.width / 2,
y: nodeRect.top + nodeRect.height / 2
};
let closestElement = null;
let shortestDistance = Infinity;
// finding distance between given and interactive node.
for(const interactiveElement of interactiveElements){
if(node===interactiveElement || node.contains(interactiveElement)){
return {node: element, distance: 0}
}
const elementRect = interactiveElement.getBoundingClientRect();
const elementCenter = {
x: elementRect.left + elementRect.width/2,
y: elementRect.top + elementRect.height/2
};
// calculate Euclidian distance
const distance = Math.sqrt(Math.pow(elementCenter.x-nodeCenter.x, 2) + Math.pow(elementCenter.y - nodeCenter.y, 2))
if(distance < shortestDistance){
shortestDistance = distance;
closestElement = interactiveElement;
}
}
return {element: closestElement, distance: shortestDistance};
Top comments (0)