Find the closest element to click coordinates – Javascript Coding Question

This is a question that requires more advanced Javascript knowledge than others. It may appear in interviews that aim to test your seniority level. In production however, it’s less likely you’ll come across the functions we’ll explore today.

 

The problem in question:

“For each click, find the element (of the same kind) that is closest to the mouse position”

 

Solution:

 

To solve this problem, we need to know the coordinates of both the mouse cursor and the elements we should measure the distance against. Once a click is made, the click handler should loop through the element’s coordinates and find which one is closest. For the sake of this tutorial, these elements will be a collection of links (“a” tags).

  1. First, we select all the elements we want to track (the “a” tags).
    let elements = Array.from(document.querySelectorAll('a'));

    With document.querySelectorAll(‘a’), we retrieve all the desired DOM nodes of the elements. Since we want to be able to loop through these, we convert the query result to an Iterable/Array with Array.from.

  2. Then, we need to store each element’s coordinates in an array.
    let linkCoords = links.map(link => {
    	let rect = link.getBoundingClientRect();
      return [rect.x, rect.y];
    });

    We use element.getBoundingClientRect() to get the “x” and “y” coordinates of every element. I encourage you to check the docs for that function through the link above (opens in new tab) for knowledge’s sake.

  3. Now that we have the coordinates of the elements, we need the coordinates of every mouse click. To achieve this, we need to listen to click events:
    document.addEventListener("click", ev => {

    In this “ev” variable, we’ll find the mouse coordinates we’re looking for: “clientX” and “clientY”.

  4. Then, in the same click event handler, we need to measure the distance to each link, thanks to the good old Pythagorean theorem.
    let distances = [];
    
      linkCoords.forEach(linkCoord => {
      	let distance = Math.hypot(linkCoord[0]-parseInt(ev.clientX), linkCoord[1]-parseInt(ev.clientY));
        distances.push(parseInt(distance));
      });

    The function responsible for calculating the distance between points is Math.hypot, whose specific job is to find out the hypotenuse in a right triangle. Need a refresher? Here’s the documentation on Math.hypot.

  5. The “distances” array, found in the previous snippet, should now contain the distances to every element. Now, it’s just a matter of choosing the shortest one:
    let closestLinkIndex = distances.indexOf(Math.min(...distances));

    Huh, what?

    Quite a lot is happening in this line of code. Starting from the right, we’re using the spread operator (…) to add the items of the “distances”array as parameters of “Math.min()”. This means that if the distance array is [5, 2 6], Math.min will be called as Math.min(5, 2, 6) instead of Math.min([5, 2, 6]). When the minimum value is retrieved, we’ll look for the index of this value within the “distances” array (distances.indexOf ).

  6. The index retrieved will be the same as the one in our initial “elements” array. This is the closest element we’ve been looking for:
    return elements[closestLinkIndex];

    That’s it! Here’s the full solution, with all the pieces put together.

Full working code:

let elements = Array.from(document.querySelectorAll('a'));

let linkCoords = elements.map(link => {
	let rect = link.getBoundingClientRect();
  return [rect.x, rect.y];
});

document.addEventListener("click", ev => {
	let distances = [];

  linkCoords.forEach(linkCoord => {
  	let distance = Math.hypot(linkCoord[0]-parseInt(ev.clientX), linkCoord[1]-parseInt(ev.clientY));
    distances.push(parseInt(distance));
  });

  let closestLinkIndex = distances.indexOf(Math.min(...distances));

  document.getElementById('result').innerHTML = (elements[closestLinkIndex].id);
});

Share your thoughts