第 11 屆 iT 邦幫忙鐵人賽

DAY 17
Modern Web

從Stack Overflow學前端系列 第 17


How to tell if a DOM element is visible in the current viewport?

Is there an efficient way to tell if a DOM element (in an HTML document) is currently visible (appears in the viewport)?

Now most browsers support getBoundingClientRect method, which has become the best practice. Using an old answer is very slow, not accurate and has several bugs.
The solution selected as correct is almost never precise. You can read more about its bugs.
This solution was tested on IE7+, iOS5+ Safari, Android2+, Blackberry, Opera Mobile, and IE Mobile 10.
這個方法也在IE7+, iOS5+ Safari, Android2+, Blackberry, Opera Mobile,IE Mobile 10驗證過

function isElementInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];

    var rect = el.getBoundingClientRect();

    return ( >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */

How to use:
You can be sure that the function given above returns correct answer at the moment of time when it is called, but what about tracking element's visibility as an event?
Place the following code at the bottom of your tag:

function onVisibilityChange(el, callback) {
    var old_visible;
    return function () {
        var visible = isElementInViewport(el);
        if (visible != old_visible) {
            old_visible = visible;
            if (typeof callback == 'function') {

var handler = onVisibilityChange(el, function() {
    /* your code go here */

$(window).on('DOMContentLoaded load resize scroll', handler); 

/* //non-jQuery
if (window.addEventListener) {
    addEventListener('DOMContentLoaded', handler, false); 
    addEventListener('load', handler, false); 
    addEventListener('scroll', handler, false); 
    addEventListener('resize', handler, false); 
} else if (window.attachEvent)  {
    attachEvent('onDOMContentLoaded', handler); // IE9+ :(
    attachEvent('onload', handler);
    attachEvent('onscroll', handler);
    attachEvent('onresize', handler);

If you do any DOM modifications, they can change your element's visibility of course.

Guidelines and common pitfalls:

Maybe you need to track page zoom / mobile device pinch? jQuery should handle zoom/pinch cross browser, otherwise first or second link should help you.

If you modify DOM, it can affect the element's visibility. You should take control over that and call handler() manually. Unfortunately, we have no cross browser onrepaint event. On the other hand that allows us to make optimizations and perform re-check only on DOM modifications that can change element's visibility.
如果你修改了DOM,這就可能會改變元素的可視性,你同時必須要主動去控制call handler,不幸的是我們沒有跨瀏覽器的事件handler,另一方面這也允許我們最佳化效能還有能檢查會改變DOM元素的可視性的修改

Never Ever use it inside jQuery $(document).ready() only, because there is no warranty CSS has been applied in this moment. Your code can work locally with your CSS on hard drive, but once put on remote server it will fail.

After DOMContentLoaded is fired, styles are applied, but the images are not loaded yet. So, we should add window.onload event listener.

We can't catch zoom/pinch event yet.
最好不要單獨在jQuery $(document).ready()用這個方法,因為不能保證css一定會抓到,其css很有可能只會在本地被應用到,然而在遠端伺服器的時候則很有可能失敗
The last resort could be the following code:

/* TODO: this looks like a very bad code */
setInterval(handler, 600); 

從Stack Overflow學前端30