Get Most Visible Element on a Web Page

Recently, I was working on updating keyboard shortcuts for o2 and came across a unique shortcut.

When pressing r, we wanted to open the reply box for the most visible post. When I first started thinking about this requirement, I found myself thinking: “How the hell can I determine what the most visible post is?”

Attaching to Scroll Event

One of the first things that crossed my mind was that I could attach to the window scroll event and then update a mostVisiblePost variable as needed. For example, when a scroll event occurs:

  • Get all posts on the page
  • Check which posts are in the viewport
  • Get the offsets for each and try to determine which is most visible.

I wasn’t quite fond of this method because it could make scrolling janky and seemed prone to error.

An Alternative Surfaces

While I was reading Javascript: The Definitive Guide I came across the JavaScript method elementFromPoint(x,y).

As I thought more about getting the most visible element on the page, I came up with the idea of creating a grid of points that covered the page. I could then get the element at each point and traverse up the DOM to find which post was at that point.

After thinking through this a bit more, I decided to go for it and came up with the following solution:

[javascript]
// Note that Underscores and Backbone are being used here.

//Let’s create a grid of points in the top half of the viewport.
var viewPortWidth = $( window ).width(),
viewPortHeight = $( window ).height(),
xCoords = _.map( [ .2 , .4, .6, .8 ], function( num, key ){ return num * viewPortWidth; } ),
yCoords = _.map( [ 0, .1, .2, .3, .4 ], function( num, key ){ return num * viewPortHeight; } );

/*
* For each coordiante pair (x,y), get element at point,
* traverse up to find a post, and add post ID to elems.
*/
var elems = [];
_.each( yCoords, function( y ){
_.each( xCoords, function( x ){
var element = $( document.elementFromPoint( x, y ) ),
closest = element.closest( o2Keyboard.threadContainer + ‘.post’ );

if ( closest.length > 0 ) {
elems.push( closest.attr( ‘id’ ) );
}
});
});

// Find most frequent (mode) post ID in elems array.
// Thanks Matthew Flaschen – http://stackoverflow.com/a/1053865
if ( elems.length > 0 ) {
var modeMap = {};

var maxEl = elems[0],
maxCount = 1;

_.each( elems, function( el ){
if ( modeMap[ el ] == null ) {
modeMap[ el ] = 1;
} else {
modeMap[ el ]++;
}

if ( modeMap[ el ] > maxCount ) {
maxEl = el;
maxCount = modeMap[ el ];
}
});
}
[/javascript]

For the purposes of O2, we defined the most visible post to be in the top 40% of the viewport. So, we created an array of y points from 0-.4 times the viewport height. We then created an array of x points that covered most of the viewport.

Once we’ve created the xCoords and yCoords arrays, we then loop over these two arrays and get the element at each (x,y) coordinate pair. In this example, we will be creating 20 unique (x,y) coordinate pairs. You can fine tune that for your needs.

Once we have the element at each point, we traverse up the DOM to see if there is a post. If there is, that post’s ID gets added to an array.

We then take a mode, essentially finding the post with the most hits, on the elems array and that is the most visible post.

Questions or Comments?

If you have any questions or comments about this method, feel free to leave a comment below.

2 responses to “Get Most Visible Element on a Web Page”

  1. Hi Eric,

    I’m not familiar with Backbone and Underscore, would you be willing to make an example of using either jQuery or regular js?

    1. Hey Kenneth –

      I doubt that I will redo the example in the near future. That being said, have you considered using Underscore in your code? 🙂

      For what it’s worth, the example is also using jQuery. Anywhere you see $(), that is jQuery.

      I don’t believe Backbone is being used in this specific example, but O2 was built using Backbone so I felt it prudent to include that. Underscore is a library that provides utilities such as _.map() and _.each().

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.