11

When using Google Reader and browsing RSS entries in the "Expanded" view, entries will automatically be marked as 'read' once a certain percentage of the div is visible on the screen (difficult to tell what percentage has to be visible in the case of Google Reader). So, as I scroll down line-by-line, the javascript code can determine that a) the entry is being rendered in the visible window and b) a certain amount is visible and when those conditions are met, the state is toggled to read.

Does anyone have any idea how that feature is implemented? Specifically, does anyone here know how to tell if a div has scrolled into view an how much of the div is visible?

As an aside, I'm using jQuery, so if anyone has any jQuery-specific examples, they would be much appreciated.

Linger
  • 14,477
  • 22
  • 48
  • 76
smoody
  • 123
  • 4

6 Answers6

4

The real trick is to keep track of where the scrollbar is in the element containing your items. Here's some code I once whipped up to do it: http://pastebin.com/f4a329cd9

You can see that as you scroll it changes focus. You just need to add more handler code to the function that handles each focus change. It works scrolling in both direction, and also by clicking right on the scrollbar, which simple mouse tracking won't give you (though in this case since the example elements are all the same size, with the same text, it's hard to tell that it has indeed scrolled). The other issue is what to do when the container bottoms out. The solution I have right now only works in FF. If you want to have it look nice in IE, you'll have to use a dummy element that blends into the background, like the one I have commented out in the code.

Cthulhon
  • 587
  • 3
  • 2
2

I just came across this as I need the same thing, and it looks super useful:

http://www.appelsiini.net/projects/viewport

Cameron Booth
  • 6,562
  • 5
  • 26
  • 21
0

In my experience, Reader has only ever marked something as read if I have moused-over or clicked on it. Assuming that as you scroll your mouse is over the div (I tend to put my mouse to the right edge of the screen when I scroll) that might explain the appearance that it only gets marked off when a certain % has been shown.

I could be (and likely am) wrong, though. I just know that the act of just scrolling through my items in reader does not mark them off. I have to make sure the mouse mouses over them as it scrolls to do it.

Leanan
  • 722
  • 7
  • 13
  • That was an interesting hypothesis, so this time I was really careful to avoid crossing into any content. I went from the scroll-down button then around the outside of the window and dropped down over to the area that lets me switch between expanded/list views and the entries were marked as read. – smoody Dec 09 '08 at 20:53
0

The dom and javascript let you calculate an element offset from its parent. To calculate the offset from the window you need to use recursion and climb your way to the top window, and also compare that against the window's size. It gets more complicated because of cross-browser issues and iframes.

To the best of my knowledge, prototype offers a simple viewportOffset method that does most of the work for you. You can also check the source for getOffsetParent and scrollTo. I don't know about jquery, but I expect it to offer similar methods.

My guess is that the script in google reader simply runs on a timeout, probably a few times a second, or perhaps in response to a scroll event. In both cases, I am sure that it is adaptive (timeout changes based on how fast the user scrolls, etc.), and that it is smart enough not to be a resource hog (i.e., don't just check all the divs in the document)

Yoni
  • 9,671
  • 9
  • 49
  • 69
0

In order to calculate whether an element is visible, you can create such a function (credit is due here https://stackoverflow.com/a/22480938/825240):

function isScrolledIntoView(element) {
  var elementTop = element.getBoundingRect().top;
  var elementBottom = element.getBoundingRect().bottom;

  var isVisible = (elementTop <= window.innerHeight) && (elementBottom >= 0);
  return isVisible;
}

You can customize that function to your situation by calculating if an element has been read:

function isRead(element) {
  var elementTop = element.getBoundingRect().top;
  var elementBottom = element.getBoundingRect().bottom;
  var elementHeight = elementBottom - elementTop;

  // if 75% of the document has been scrolled, we'll assume it's been read
  var readIfPercentage = 0.75;

  // an element has been read if the top has been scrolled up out of view
  // and at least 75% of the element is no longer visible
  var isRead = (elementTop < 0 && Math.abs(elementTop) / elementHeight >= readIfPercentage);
  return isRead;
}

You can then call the functions above, passing in a DOM node as the element:

isScrolledIntoView(document.getElementById('targetDiv');
//or
isRead(document.getElementById('targetDiv');

You can tie it all together by creating a scroll listener (jQuery makes this pretty easy):

function setScrollListener() {

  var scrollEventHandler = function() {
    if (isRead(document.getElementById('article'))) {
      // set article to 'read'
    }
  }

  // on scroll, fire the event handler
  $(document).scroll(scrollEventHandler);
}

It's worth noting that if you want to unbind the scroll listener, say if all of the articles have been read and you no longer need to listen to the scroll, you can call the unbind function within the scrollEventHandler. It is as simple as:

function unbindScrollEventHandler() {
  $(document).unbind('scroll', scrollEventHandler);
}
0

You can try this one, the key point is the element must visible to inner body more and meet the visible ratio (in this case 0.85).

isRead(element) {
   let rect = element.getBoundingClientRect();
   const visibleRatio = 0.85;
   let elementRatio = (window.innerHeight - Math.abs(rect.top))/rect.height;
   let isRead = (rect.top >= 0) && (elementRatio >= visibleRatio);
   return isRead; 
}
Juk
  • 76
  • 1
  • 3