document.elementFromPoint – a jQuery solution

While writing a new drag & drop component, i came across the javascript function elementFromPoint(x,y).

The elementFromPoint method returns the DOM node located at the coordinates x,y. This method is truly helpful when it comes to detecting a drop target. You simply get the node at the mouse position with elementFromPoint at the coordinates the drop occured and walk up through the DOM tree untill you find a valid drop target. From a jQuery point of view, this means only using $(nodeUnderPointXY).closest('myDropTargetSelector') on the node.

For more information on this elementFromPoint, see quirksmode’s compatibility table and a demo.

But unfotunately there is a big drawback !!!

I.e. there are incompatibilities between browsers in wether to use the absolute document coordinates or the relative window coordinates to detect the element.

Safari 4 and Opera 10.10 need the absolute coordinates, whereas IE and Firefox need the the relative ones. In case of a mouse event, this means the difference between pageY and clientY. clientY is the relative value which is measured from to the top left corner of the viewport. pageY is the absolute value, which is measured from the top left corner of the document. The following picture demonstrates the difference.

As you can see, there won’t be any problems if the page is not scrolled. In this case pageY and clientY are pointing to the same point of the document. But the moment the page is scrolled, both values get out of sync.

A solution to the problem

Of course we could check browsers & versions and then decide which coordinates to pass to the method. But the better way will always be feature detection. In the following i try to demonstrate a way on how to detect which coordinates are needed and then wrap these results in a new elementFromPoint method. At first we take a look at the w3c specification on the elementFromPoint method.

The w3c specification says:

The elementFromPoint(x, y) method, when invoked, must return the element at coordinates x,y in the viewport. The element to be returned is determined through hit testing. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any), or y is greather than the viewport height excluding the size of a rendered scroll bar (if any), the method must return null. If there is no element at the given position the method must return the root element, if any, or null otherwise.

Two things are important.
First: passing relative coordinates (viewport) is the default solution.
And second: if you pass coordinates which are out of the bounds of the viewport, the method must return null, whereas if the coordinates are inside the viewport’s bounds, it will at least return the root element.

This is where we start with our feature detector:

In case the document isn’t scrolled we don’t have to do any checks, as clientY and pageY will always point to the same location.

In case the page is scrolled we take the scrollamount, add it to the viewport’s height and then substract 1px. In jQuery this means $(document).scrollTop() + $(window).height() -1. This coordinate points to the bottom-most location of the viewport, if absolute coordinates are used, but exceeds the viewport, if relative coordinates are used. So if document.elementFromPoint( 0, $(document).scrollTop() + $(window).height() -1 ) returns null, the browser uses relative coordinates (clientY), and if the browser returns at least some node (possibly the root node), the browsers uses absolute coordinates (pageY).

And here are all these thoughst joined into a script:

(function ($){
  var check=false, isRelative=true;

  $.elementFromPoint = function(x,y)
  {
    if(!document.elementFromPoint) return null;

    if(!check)
    {
      var sl;
      if((sl = $(document).scrollTop()) >0)
      {
       isRelative = (document.elementFromPoint(0, sl + $(window).height() -1) == null);
      }
      else if((sl = $(document).scrollLeft()) >0)
      {
       isRelative = (document.elementFromPoint(sl + $(window).width() -1, 0) == null);
      }
      check = (sl>0);
    }

    if(!isRelative)
    {
      x += $(document).scrollLeft();
      y += $(document).scrollTop();
    }

    return document.elementFromPoint(x,y);
  }	

})(jQuery);
This entry was posted on Friday, November 19th, 2010 at 20:33. It is filed under javascript. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

11 Comments

001
Leo Pradipta - January 18th, 2011 at 8:38 am

Good Explanation… Good Topic…

002
Henrik Hjelte - January 22nd, 2011 at 4:52 pm

Smart, very useful.

003
javierfp - April 19th, 2011 at 4:21 pm

Really helpful!

004
Alexey Kruchinin - October 2nd, 2011 at 12:12 am

There’s a typo in the code
“$.elementFromPoint : function(x,y)”
should be
“$.elementFromPoint = function(x,y)”

005
angelo - October 26th, 2011 at 12:49 pm

that was really helpful

006
admin - November 1st, 2011 at 5:06 pm

thanks for the hint alexey !

007
nickboss - February 11th, 2012 at 1:43 pm

Be carefull with IE8, because even if (x,y) is out of viewport, document.elementFromPoint will return a non null object (HtmlHTMLElement) and it will make the routine behave incorrectly.

In this case you can do this:


var doc;
if ((sl = $(document).scrollTop()) >0)
{
doc = document.elementFromPoint(0, sl + $j(window).height() -1);
if ( doc != null ) { if ( doc.tagName.toUpperCase() == ‘HTML’ ) doc = null; }
isRelative = ( doc == null );
}
else if((sl = $(document).scrollLeft()) >0)
{
doc = document.elementFromPoint(sl + $(window).width() -1, 0);
if ( doc != null ) { if ( doc.tagName.toUpperCase() == ‘HTML’ ) doc = null; }
isRelative = ( doc == null );
}

008
jQuery | Pearltrees - March 23rd, 2013 at 11:21 pm

[...] document.elementFromPoint – a jQuery solution – zehnet.de home • contact • blog • fb • twitter to experience pearltrees activate javascript. [...]

009
lose weight with garcinia cambogia - August 17th, 2013 at 12:32 am

[...] wonder if garcinia cambogia really works? its well prooven that garcinia will help you loose weight, but everything else is up to you. Read More: http://www.zehnet.de/2010/11/19/document-elementfrompoint-a-jquery-solution/ [...]

010
Steve - November 19th, 2013 at 9:28 pm

Good info my problem sorted out after reading this article.
if((sl = $(document).scrollTop()) >0)

specially this part my problem sorted out.
Thanks

Regards,
Steve
GarciniaCambogiaOfficial.com

011
[FRAGE] Thema elementFromPoint() - March 7th, 2014 at 11:12 am

[...] [...]

 
Name:
Mail:
Website:
Comment:
Validation: validation