Có cách nào để phát hiện nếu một cửa sổ trình duyệt hiện không hoạt động không?


585

Tôi có JavaScript đang hoạt động định kỳ. Khi người dùng không nhìn vào trang web (nghĩa là cửa sổ hoặc tab không có tiêu điểm), thật tuyệt khi không chạy.

Có cách nào để làm điều này sử dụng JavaScript?

Điểm tham chiếu của tôi: Trò chuyện Gmail phát âm thanh nếu cửa sổ bạn đang sử dụng không hoạt động.


8
Đối với những người không hài lòng với câu trả lời bên dưới, hãy xem requestAnimationFrameAPI hoặc sử dụng tính năng hiện đại mà tần số setTimeout/ setIntervalbị giảm khi cửa sổ không hiển thị (ví dụ: 1 giây trong Chrome).
Rob W

2
document.body.onblur = function (e) {console.log ('lama');} làm việc cho các yếu tố không tập trung.
WhyMe

2
Xem câu trả lời này để biết giải pháp tương thích với nhiều trình duyệt sử dụng API khả năng hiển thị trang của W3C, quay lại blur/ focustrong các trình duyệt không hỗ trợ nó.
Mathias Bynens

2
80% câu trả lời dưới đây không phải là câu trả lời cho câu hỏi này . Câu hỏi hỏi về việc hiện không hoạt động nhưng hàng tấn câu trả lời dưới đây không thể nhìn thấy được mà không phải là câu trả lời cho câu hỏi này. Họ có thể được gắn cờ là "không phải là một câu trả lời"
gman

Câu trả lời:


691

Kể từ khi viết câu trả lời này, một đặc điểm kỹ thuật mới đã đạt đến trạng thái đề xuất nhờ W3C. Các API hiển thị trang (trên MDN ) bây giờ cho phép chúng ta phát hiện chính xác hơn khi một trang được ẩn cho người dùng.

document.addEventListener("visibilitychange", onchange);

Hỗ trợ trình duyệt hiện tại:

  • Chrome 13+
  • Internet Explorer 10+
  • Firefox 10+
  • Opera 12.10+ [ ghi chú đọc ]

Đoạn mã sau rơi trở lại phương pháp làm mờ / lấy nét kém tin cậy hơn trong các trình duyệt không tương thích:

(function() {
  var hidden = "hidden";

  // Standards:
  if (hidden in document)
    document.addEventListener("visibilitychange", onchange);
  else if ((hidden = "mozHidden") in document)
    document.addEventListener("mozvisibilitychange", onchange);
  else if ((hidden = "webkitHidden") in document)
    document.addEventListener("webkitvisibilitychange", onchange);
  else if ((hidden = "msHidden") in document)
    document.addEventListener("msvisibilitychange", onchange);
  // IE 9 and lower:
  else if ("onfocusin" in document)
    document.onfocusin = document.onfocusout = onchange;
  // All others:
  else
    window.onpageshow = window.onpagehide
    = window.onfocus = window.onblur = onchange;

  function onchange (evt) {
    var v = "visible", h = "hidden",
        evtMap = {
          focus:v, focusin:v, pageshow:v, blur:h, focusout:h, pagehide:h
        };

    evt = evt || window.event;
    if (evt.type in evtMap)
      document.body.className = evtMap[evt.type];
    else
      document.body.className = this[hidden] ? "hidden" : "visible";
  }

  // set the initial state (but only if browser supports the Page Visibility API)
  if( document[hidden] !== undefined )
    onchange({type: document[hidden] ? "blur" : "focus"});
})();

onfocusinonfocusoutđược yêu cầu cho IE 9 trở xuống , trong khi tất cả những người khác sử dụng onfocusonblur, ngoại trừ iOS, sử dụng onpageshowonpagehide.


1
@bellpeace: IE nên truyền bá focusinfocusouttừ iframe sang cửa sổ phía trên. Đối với các trình duyệt mới hơn, bạn chỉ cần xử lý các sự kiện focusblurtrên các windowđối tượng của iframe . Bạn nên sử dụng mã cập nhật Tôi chỉ cần thêm đó có ít nhất sẽ bao gồm những trường hợp trong các trình duyệt mới hơn.
Andy E

3
@JulienKronegg: đó là lý do tại sao câu trả lời của tôi đặc biệt đề cập đến API Hiển thị trang đã nhập trạng thái dự thảo sau khi tôi viết câu trả lời ban đầu. Các phương pháp lấy nét / làm mờ cung cấp chức năng giới hạn cho các trình duyệt cũ hơn. Liên kết với các sự kiện khác, như trong câu trả lời của bạn, không bao gồm nhiều vấn đề hơn thế này và có nguy cơ khác biệt về hành vi (như IE không bắn chuột ra khi cửa sổ bật lên bên dưới con trỏ). Tôi sẽ đề xuất một hành động thích hợp hơn là hiển thị thông báo hoặc biểu tượng cho người dùng biết rằng các cập nhật có thể ít thường xuyên hơn do không hoạt động của trang.
Andy E

6
@AndyE Mình đã thử giải pháp này trên crom. Nó hoạt động nếu tôi thay đổi các tab, nhưng nó không hoạt động nếu tôi thay đổi các cửa sổ (tab ALT +). Có nên không? Đây là một câu đố - jsfiddle.net/8a9N6/17
Tony Lâmpada

2
@Heliodor: Bây giờ tôi muốn giữ mã trong câu trả lời tối thiểu. Nó không bao giờ được dự định là một giải pháp hoàn chỉnh cắt và dán, vì những người triển khai có thể muốn tránh đặt một lớp trên cơ thể và thực hiện một hành động hoàn toàn khác (chẳng hạn như dừng và bắt đầu hẹn giờ).
Andy E

8
@AndyE Giải pháp của bạn dường như chỉ hoạt động nếu người dùng thay đổi tab hoặc thu nhỏ / tối đa hóa cửa sổ. Tuy nhiên, sự kiện onchange không được kích hoạt nếu người dùng rời khỏi tab hoạt động, nhưng tối đa hóa một chương trình khác trên nó từ thanh taskbar. Có một giải pháp cho kịch bản đó? Cảm ơn!
dùng1491636

132

Tôi sẽ sử dụng jQuery bởi vì sau đó tất cả những gì bạn phải làm là:

$(window).blur(function(){
  //your code here
});
$(window).focus(function(){
  //your code
});

Hoặc ít nhất nó đã làm việc cho tôi.


1
đối với tôi cuộc gọi này hai lần trong iframe
mseach

Trong Firefox, nếu bạn nhấp vào bên trong bảng điều khiển firebird (trên cùng một trang), windowsẽ mất tiêu điểm, điều này đúng, nhưng tùy thuộc vào ý định của bạn có thể không phải là điều bạn cần.
Majid fouadpour

21
Điều này không còn hoạt động đối với các phiên bản hiện tại của trình duyệt hiện đại, hãy xem câu trả lời được phê duyệt (API hiển thị trang)
Jon z

Giải pháp này không hoạt động trên iPad, vui lòng sử dụng sự kiện "Pageshow"
ElizaS

Cả BLUR và FOCUS đều tắt khi tải trang. Khi tôi mở một cửa sổ mới từ trang của mình, không có gì xảy ra nhưng một khi cửa sổ mới đóng cả hai sự kiện sẽ tắt: / (sử dụng IE8)
SearchForKnowledge

49

Có 3 phương pháp điển hình được sử dụng để xác định xem người dùng có thể xem trang HTML hay không, tuy nhiên không có phương pháp nào hoạt động hoàn hảo:

  • Các W3C API hiển thị trang là nghĩa vụ phải làm điều này (được hỗ trợ từ: Firefox 10, MSIE 10, Chrome 13). Tuy nhiên, API này chỉ làm tăng các sự kiện khi tab trình duyệt bị quá tải hoàn toàn (ví dụ: khi người dùng thay đổi từ tab này sang tab khác). API không tăng sự kiện khi khả năng hiển thị không thể được xác định với độ chính xác 100% (ví dụ: Alt + Tab để chuyển sang ứng dụng khác).

  • Sử dụng các phương pháp lấy nét / làm mờ cho bạn rất nhiều kết quả dương tính giả. Ví dụ: nếu người dùng hiển thị một cửa sổ nhỏ hơn ở trên cùng của cửa sổ trình duyệt, cửa sổ trình duyệt sẽ mất tiêu điểm ( onblurnâng lên) nhưng người dùng vẫn có thể nhìn thấy nó (vì vậy nó vẫn cần được làm mới). Xem thêm http://javascript.info/tutorial/f Focus

  • Dựa vào hoạt động của người dùng (di chuyển chuột, nhấp chuột, gõ phím) cũng mang lại cho bạn rất nhiều kết quả dương tính giả. Hãy suy nghĩ về trường hợp tương tự như trên hoặc người dùng đang xem video.

Để cải thiện các hành vi không hoàn hảo được mô tả ở trên, tôi sử dụng kết hợp cả 3 phương pháp: API hiển thị W3C, sau đó lấy nét / làm mờ và phương pháp hoạt động của người dùng để giảm tỷ lệ dương tính giả. Điều này cho phép quản lý các sự kiện sau:

  • Thay đổi tab trình duyệt sang một trình duyệt khác (độ chính xác 100%, nhờ API hiển thị trang W3C)
  • Trang có khả năng bị ẩn bởi một cửa sổ khác, ví dụ do Alt + Tab (xác suất = không chính xác 100%)
  • Sự chú ý của người dùng có khả năng không tập trung vào trang HTML (xác suất = không chính xác 100%)

Đây là cách nó hoạt động: khi tài liệu mất tiêu điểm, hoạt động của người dùng (chẳng hạn như di chuyển chuột) trên tài liệu được theo dõi để xác định xem cửa sổ có hiển thị hay không. Xác suất hiển thị trang tỷ lệ nghịch với thời gian của hoạt động người dùng cuối cùng trên trang: nếu người dùng không thực hiện hoạt động nào trên tài liệu trong một thời gian dài, thì trang này hầu như không hiển thị. Mã bên dưới bắt chước API hiển thị trang của W3C: nó hoạt động theo cùng một cách nhưng có tỷ lệ dương tính giả nhỏ. Nó có lợi thế là đa chức năng (được thử nghiệm trên Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9).

    <div id = "x"> </ div>

    <tập lệnh>
    / **
    Đăng ký xử lý sự kiện cho đối tượng đã cho.
    @param obj đối tượng sẽ nâng cao sự kiện
    @param evType loại sự kiện: nhấp, nhấn, di chuột, ...
    @param fn chức năng xử lý sự kiện
    @param isCapturing đặt chế độ sự kiện (true = chụp sự kiện, false = sự kiện sủi bọt)
    @return đúng nếu trình xử lý sự kiện đã được đính kèm chính xác
    * /
    hàm addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        trả lại đúng sự thật;
      } if if (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('trên' + evType, fn);
        trả lại r;
      } khác {
        trả lại sai;
      }
    }

    // đăng ký thay đổi khả năng hiển thị trang tiềm năng
    addEvent (tài liệu, "tiềm năng trao đổi", hàm (sự kiện) {
      document.getEuityById ("x"). InternalHTML + = "entialVisilityChange: tiềm năng = "
    });

    // đăng ký API hiển thị trang W3C
    var ẩn = null;
    khả năng hiển thị varChange = null;
    if (typeof document.mozHidden! == "không xác định") {
      ẩn = "mozHidden";
      visualChange = "mozvisibilitychange";
    } if if (typeof document.msHidden! == "không xác định") {
      ẩn = "msHidden";
      mức độ hiển thị = "msvisibilitychange";
    } if if (typeof document.webkitHidden! == "không xác định") {
      hidden = "webkitHidden";
      mức độ hiển thị = "webkitvisibilitychange";
    } if if (typeof document.hidden! == "hidden") {
      ẩn = "ẩn";
      tầm nhìnChange = "khả năng hiển thị";
    }
    if (hidden! = null && visualChange! = null) {
      addEvent (tài liệu, mức độ hiển thị, chức năng (sự kiện) {
        document.getEuityById ("x"). InternalHTML + = visualChange + ":" + hidden + "=" + document [hidden] + "<br>";
      });
    }


    varentialPageVisibility = {
      pageVisibilityChangeThrưỡng: 3 * 3600, // tính bằng giây
      init: function () {
        hàm setAsNotHidden () {
          var ClarkEventRequired = document.potentialHidden;
          tài liệu.potentialHidden = false;
          tài liệu.potivelyHiddenSince = 0;
          if (ftimeEventRequired) ftimePageVisibilityChangeEvent ();
        }

        hàm initPotivelyHiddenDetection () {
          if (! hasF FocusLocal) {
            // cửa sổ không có tiêu điểm => kiểm tra hoạt động của người dùng trong cửa sổ
            lastActionDate = ngày mới ();
            if (timeoutHandler! = null) {
              ClearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility ,entialPageVisibility.pageVisibilityChangeThrưỡng * 1000 + 100); // +100 ms để tránh các vấn đề làm tròn trong Firefox
          }
        }

        hàm ClarkPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatch ALLowed = false;
          var evt = document.createEvent ("Sự kiện");
          evt.initEvent ("tiềm năng trao đổi", đúng, đúng);
          document.dispatchEvent (evt);
        }

        chức năng checkPageVisibility () {
          varentialHiddenDuration = (hasF FocusLocal || lastActionDate == null? 0: Math.floor ((ngày mới (). getTime () - lastActionDate.getTime ()) / 1000));
                                        document.potivelyHiddenSince = tiềm năngHiddenDuration;
          if (entialHiddenDuration> =entialPageVisibility.pageVisibilityChangeThr Ngưỡng &&! document.potentialHidden) {
            // ngưỡng thay đổi mức độ hiển thị trang raiched => tăng mức chẵn
            tài liệu.potentialHidden = true;
            ClarkPageVisibilityChangeEvent ();
          }
        }

        var lastActionDate = null;
        var hasF FocusLocal = true;
        var hasMouseOver = true;
        tài liệu.potentialHidden = false;
        tài liệu.potivelyHiddenSince = 0;
        var timeoutHandler = null;

        addEvent (tài liệu, "pagehow", hàm (sự kiện) {
          document.getEuityById ("x"). InternalHTML + = "Pageshow / doc: <br>";
        });
        addEvent (tài liệu, "trang da", hàm (sự kiện) {
          document.getEuityById ("x"). InternalHTML + = "page leather / doc: <br>";
        });
        addEvent (cửa sổ, "pagehow", hàm (sự kiện) {
          document.getEuityById ("x"). InternalHTML + = "Pageshow / win: <br>"; // được nâng lên khi trang đầu tiên hiển thị
        });
        addEvent (cửa sổ, "trang da", hàm (sự kiện) {
          document.getEuityById ("x"). InternalHTML + = "page leather / win: <br>"; // không được nâng lên
        });
        addEvent (tài liệu, "mousemove", hàm (sự kiện) {
          lastActionDate = ngày mới ();
        });
        addEvent (tài liệu, "mouseover", hàm (sự kiện) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (tài liệu, "mouseout", hàm (sự kiện) {
          hasMouseOver = false;
          initPotivelyHiddenDetection ();
        });
        addEvent (cửa sổ, "làm mờ", hàm (sự kiện) {
          hasF FocusLocal = false;
          initPotivelyHiddenDetection ();
        });
        addEvent (cửa sổ, "tiêu điểm", hàm (sự kiện) {
          hasF FocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    tiềm năngPageVisibility.pageVisibilityChangeThr Ngưỡng = 4; // 4 giây để thử nghiệm
    tiềm năngPageVisibility.init ();
    </ script>

Vì hiện tại không có giải pháp trình duyệt chéo nào hoạt động mà không có dương tính giả, tốt hơn bạn nên suy nghĩ kỹ về việc vô hiệu hóa hoạt động định kỳ trên trang web của bạn.


Sẽ không sử dụng toán tử so sánh nghiêm ngặt trên chuỗi 'không xác định' thay vì từ khóa không xác định gây ra lỗi tích cực trong mã trên?
Jonathon

@kiran: Thật ra nó đang hoạt động với Alt + Tab. Bạn không thể xác định xem trang có bị ẩn hay không khi bạn thực hiện Alt + Tab vì bạn có thể chuyển sang cửa sổ nhỏ hơn để bạn không thể đảm bảo rằng trang của mình bị ẩn hoàn toàn. Đây là lý do tại sao tôi sử dụng khái niệm "có khả năng bị ẩn" (trong ví dụ, ngưỡng được đặt thành 4 giây, vì vậy bạn cần chuyển sang một cửa sổ khác bằng Alt + Tab trong ít nhất 4 giây). Tuy nhiên nhận xét của bạn cho thấy câu trả lời không quá rõ ràng, vì vậy tôi đã điều chỉnh lại.
Julien Kronegg

@JulienKronegg Tôi nghĩ đây là giải pháp tốt nhất. Tuy nhiên, đoạn mã trên cực kỳ cần một số cấu trúc lại và trừu tượng hóa. Tại sao bạn không tải nó lên GitHub và để cộng đồng tái cấu trúc nó?
Jacob

1
@Jacob Tôi rất vui vì bạn thích giải pháp của tôi. Hãy tự mình quảng bá nó vào một dự án GitHub. Tôi cung cấp mã có giấy phép Creative Commons BY creativecommons.org/licenses/by/4.0
Julien Kronegg

26

Có một thư viện gọn gàng có sẵn trên GitHub:

https://github.com/serkanyersen/ifvisible.js

Thí dụ:

// If page is visible right now
if( ifvisible.now() ){
  // Display pop-up
  openPopUp();
}

Tôi đã thử nghiệm phiên bản 1.0.1 trên tất cả các trình duyệt tôi có và có thể xác nhận rằng nó hoạt động với:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... Và có lẽ tất cả các phiên bản mới hơn.

Không hoàn toàn làm việc với:

  • IE8 - luôn cho biết tab / cửa sổ hiện đang hoạt động ( .now()luôn trả về truecho tôi)

Câu trả lời được chấp nhận gây ra vấn đề trong IE9. Thư viện này hoạt động tuyệt vời.
Tom Teman

20

Sử dụng: API hiển thị trang

document.addEventListener( 'visibilitychange' , function() {
    if (document.hidden) {
        console.log('bye');
    } else {
        console.log('well back');
    }
}, false );

Tôi có thể sử dụng? http://caniuse.com/#feat=pagevisibility


Câu hỏi không phải là về khả năng hiển thị trang. Đó là về việc không hoạt động / hoạt động
gman

Tôi nghĩ OP không nói về chức năng của ide
l2aelba

1
Tôi cũng không nói về ide. Tôi đang nói về alt-tabbing / cmd-tabbing cho một ứng dụng khác. Đột nhiên trang không hoạt động. Api khả năng hiển thị trang không giúp tôi biết nếu trang không hoạt động, nó chỉ giúp tôi biết nếu có thể không hiển thị.
gman

18

Tôi tạo Trò chuyện Sao chổi cho ứng dụng của mình và khi tôi nhận được tin nhắn từ một người dùng khác, tôi sử dụng:

if(new_message){
    if(!document.hasFocus()){
        audio.play();
        document.title="Have new messages";
    }
    else{
        audio.stop();
        document.title="Application Name";
    } 
}

2
Giải pháp sạch nhất với sự hỗ trợ trở lại IE6
Paul Cooper

4
document.hasFocus()là cách sạch nhất để làm điều đó. Tất cả các cách khác bằng cách sử dụng api hoặc sự kiện hiển thị dựa trên hoặc tìm kiếm các mức độ khác nhau của hoạt động người dùng / thiếu hoạt động trở nên quá phức tạp và đầy các trường hợp và lỗ hổng cạnh. đặt nó trên một khoảng thời gian đơn giản và nâng cao một sự kiện tùy chỉnh khi kết quả thay đổi. Ví dụ: jsfiddle.net/59utucz6/1
danatcofo

1
Hiệu quả và không giống như các giải pháp khác cung cấp phản hồi chính xác khi bạn chuyển sang tab hoặc cửa sổ trình duyệt khác và thậm chí là một ứng dụng khác.
ow3n

Không còn nghi ngờ gì nữa, đây là cách sạch nhất, nhưng nó không hoạt động trong firefox
hardik chugh

1
Nếu tôi mở các công cụ Chrome Dev thì document.hasF Focus () bằng với false. Hoặc thậm chí nếu bạn nhấp vào bảng trên cùng của trình duyệt, điều tương tự cũng xảy ra. Tôi không chắc giải pháp này phù hợp để tạm dừng video, hoạt hình, v.v.
tylik

16

Tôi bắt đầu sử dụng câu trả lời wiki cộng đồng, nhưng nhận ra rằng nó không phát hiện ra các sự kiện tab alt trong Chrome. Điều này là do nó sử dụng nguồn sự kiện có sẵn đầu tiên và trong trường hợp này là API hiển thị trang, trong Chrome dường như không theo dõi tab alt.

Tôi quyết định sửa đổi tập lệnh một chút để theo dõi tất cả các sự kiện có thể xảy ra đối với các thay đổi tập trung trang. Đây là một chức năng bạn có thể thả vào:

function onVisibilityChange(callback) {
    var visible = true;

    if (!callback) {
        throw new Error('no callback given');
    }

    function focused() {
        if (!visible) {
            callback(visible = true);
        }
    }

    function unfocused() {
        if (visible) {
            callback(visible = false);
        }
    }

    // Standards:
    if ('hidden' in document) {
        document.addEventListener('visibilitychange',
            function() {(document.hidden ? unfocused : focused)()});
    }
    if ('mozHidden' in document) {
        document.addEventListener('mozvisibilitychange',
            function() {(document.mozHidden ? unfocused : focused)()});
    }
    if ('webkitHidden' in document) {
        document.addEventListener('webkitvisibilitychange',
            function() {(document.webkitHidden ? unfocused : focused)()});
    }
    if ('msHidden' in document) {
        document.addEventListener('msvisibilitychange',
            function() {(document.msHidden ? unfocused : focused)()});
    }
    // IE 9 and lower:
    if ('onfocusin' in document) {
        document.onfocusin = focused;
        document.onfocusout = unfocused;
    }
    // All others:
    window.onpageshow = window.onfocus = focused;
    window.onpagehide = window.onblur = unfocused;
};

Sử dụng nó như thế này:

onVisibilityChange(function(visible) {
    console.log('the page is now', visible ? 'focused' : 'unfocused');
});

Phiên bản này lắng nghe tất cả các sự kiện khả năng hiển thị khác nhau và thực hiện cuộc gọi lại nếu bất kỳ sự kiện nào gây ra thay đổi. Trình xử lý focusedunfocusedđảm bảo rằng cuộc gọi lại không được gọi nhiều lần nếu nhiều API có cùng mức thay đổi mức độ hiển thị.


Chrome chẳng hạn có cả document.hiddendocument.webkitHidden. Nếu không có elsetrong ifxây dựng chúng ta sẽ nhận được 2 cuộc gọi lại phải không?
Christiaan Westerbeek

@ChristiaanWesterbeek Đó là một điểm tốt, tôi đã không nghĩ về điều đó! Nếu bạn có thể chỉnh sửa bài đăng này, hãy tiếp tục và tôi sẽ chấp nhận :)
Daniel Buckmaster

Uh hey đợi một chút: bản chỉnh sửa để thêm "khác" được đề xuất bởi ChristiaanWesterbeek và thực sự được thêm bởi @ 1.21Gigawatts có vẻ không phải là một ý tưởng hay: nó đánh bại ý tưởng mua ban đầu của Daniel, đó là thử tất cả các hỗ trợ phương pháp song song. Và không có nguy cơ cuộc gọi lại được gọi hai lần vì tập trung () và không tập trung () triệt tiêu các cuộc gọi bổ sung khi không có gì thay đổi. Thực sự có vẻ như chúng ta nên trở lại vòng quay đầu tiên.
Louis Semprini

@LouisSemprini đó là một sản phẩm tuyệt vời. Tôi đã quên mục đích ban đầu của mã! Tôi đã khôi phục bản gốc và thêm một lời giải thích!
Daniel Buckmaster

kiểm tra điều này cho đến ngày hôm nay, nó không phát hiện tab alt + ít nhất trên Chrome 78 + macos
Hugo Gresse

7

Điều này thực sự khó khăn. Dường như không có giải pháp nào cho các yêu cầu sau.

  • Trang này bao gồm iframe mà bạn không có quyền kiểm soát
  • Bạn muốn theo dõi thay đổi trạng thái hiển thị bất kể thay đổi được kích hoạt bởi thay đổi TAB (tab ctrl +) hoặc thay đổi cửa sổ (tab alt +)

Điều này xảy ra vì:

  • API hiển thị trang có thể cho bạn biết một cách đáng tin cậy về sự thay đổi tab (ngay cả với iframe), nhưng nó không thể cho bạn biết khi nào người dùng thay đổi cửa sổ.
  • Nghe các sự kiện làm mờ / lấy nét cửa sổ có thể phát hiện các tab alt + và tab ctrl +, miễn là iframe không có tiêu điểm.

Với những hạn chế này, có thể triển khai một giải pháp kết hợp - API hiển thị trang - làm mờ / tập trung cửa sổ - document.activeEuity

Điều đó có thể:

  • 1) tab ctrl + khi trang mẹ có tiêu điểm: CÓ
  • 2) tab ctrl + khi iframe có tiêu điểm: CÓ
  • 3) tab alt + khi trang chính có tiêu điểm: CÓ
  • 4) tab alt + khi iframe có tiêu điểm: KHÔNG <- bummer

Khi iframe có tiêu điểm, các sự kiện mờ / tiêu điểm của bạn hoàn toàn không được gọi và API Hiển thị trang sẽ không kích hoạt trên tab alt +.

Tôi đã xây dựng dựa trên giải pháp của @ AndyE và triển khai giải pháp (gần như tốt) này tại đây: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (xin lỗi, tôi gặp một số rắc rối với JSFiddle).

Điều này cũng có sẵn trên Github: https://github.com/qmagico/estante-components

Điều này hoạt động trên chrome / crom. Nó hoạt động trên firefox, ngoại trừ việc nó không tải nội dung iframe (có ý tưởng nào không?)

Dù sao, để giải quyết vấn đề cuối cùng (4), cách duy nhất bạn có thể làm là lắng nghe các sự kiện mờ / tập trung trên iframe. Nếu bạn có một số quyền kiểm soát iframe, bạn có thể sử dụng API postMessage để làm điều đó.

https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test2.html

Tôi vẫn chưa thử nghiệm điều này với đủ trình duyệt. Nếu bạn có thể tìm thêm thông tin về nơi mà điều này không hoạt động, xin vui lòng cho tôi biết trong các ý kiến ​​dưới đây.


Trong các thử nghiệm của tôi, nó cũng hoạt động trên IE9, IE10 và Chrome trên Android.
Tony Lâmpada

1
Có vẻ như IPAD cần một giải pháp hoàn toàn khác - stackoverflow.com/questions/4940657/NH
Tony Lâmpada

3
Tất cả các liên kết này là 404s :(
Daniel Buckmaster

6
var visibilityChange = (function (window) {
    var inView = false;
    return function (fn) {
        window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
            if ({focus:1, pageshow:1}[e.type]) {
                if (inView) return;
                fn("visible");
                inView = true;
            } else if (inView) {
                fn("hidden");
                inView = false;
            }
        };
    };
}(this));

visibilityChange(function (state) {
    console.log(state);
});

http://jsfiddle.net/ARTsinn/JTxQY/


5

cái này hoạt động với tôi trên chrome 67, firefox 67,

if(!document.hasFocus()) {
    // do stuff
}

3

bạn có thể sử dụng:

(function () {

    var requiredResolution = 10; // ms
    var checkInterval = 1000; // ms
    var tolerance = 20; // percent


    var counter = 0;
    var expected = checkInterval / requiredResolution;
    //console.log('expected:', expected);

    window.setInterval(function () {
        counter++;
    }, requiredResolution);

    window.setInterval(function () {
        var deviation = 100 * Math.abs(1 - counter / expected);
        // console.log('is:', counter, '(off by', deviation , '%)');
        if (deviation > tolerance) {
            console.warn('Timer resolution not sufficient!');
        }
        counter = 0;
    }, checkInterval);

})();

3

Trong HTML 5, bạn cũng có thể sử dụng:

  • onpageshow: Tập lệnh sẽ được chạy khi cửa sổ hiển thị
  • onpagehide: Tập lệnh sẽ được chạy khi cửa sổ bị ẩn

Xem:


2
Tôi nghĩ rằng điều này có liên quan đến BFCache: khi người dùng nhấp vào Quay lại hoặc Chuyển tiếp - nó không liên quan đến trang nằm ở đầu máy tính để bàn.
phân cực

2

Đây là bản phóng tác của câu trả lời từ Andy E.

Điều này sẽ thực hiện một nhiệm vụ, ví dụ như làm mới trang cứ sau 30 giây, nhưng chỉ khi trang hiển thị và tập trung.

Nếu tầm nhìn không thể được phát hiện, thì chỉ có tiêu điểm sẽ được sử dụng.

Nếu người dùng tập trung trang, thì nó sẽ cập nhật ngay lập tức

Trang sẽ không cập nhật lại cho đến 30 giây sau bất kỳ cuộc gọi ajax nào

var windowFocused = true;
var timeOut2 = null;

$(function(){
  $.ajaxSetup ({
    cache: false
  });
  $("#content").ajaxComplete(function(event,request, settings){
       set_refresh_page(); // ajax call has just been made, so page doesn't need updating again for 30 seconds
   });
  // check visibility and focus of window, so as not to keep updating unnecessarily
  (function() {
      var hidden, change, vis = {
              hidden: "visibilitychange",
              mozHidden: "mozvisibilitychange",
              webkitHidden: "webkitvisibilitychange",
              msHidden: "msvisibilitychange",
              oHidden: "ovisibilitychange" /* not currently supported */
          };
      for (hidden in vis) {
          if (vis.hasOwnProperty(hidden) && hidden in document) {
              change = vis[hidden];
              break;
          }
      }
      document.body.className="visible";
      if (change){     // this will check the tab visibility instead of window focus
          document.addEventListener(change, onchange,false);
      }

      if(navigator.appName == "Microsoft Internet Explorer")
         window.onfocus = document.onfocusin = document.onfocusout = onchangeFocus
      else
         window.onfocus = window.onblur = onchangeFocus;

      function onchangeFocus(evt){
        evt = evt || window.event;
        if (evt.type == "focus" || evt.type == "focusin"){
          windowFocused=true; 
        }
        else if (evt.type == "blur" || evt.type == "focusout"){
          windowFocused=false;
        }
        if (evt.type == "focus"){
          update_page();  // only update using window.onfocus, because document.onfocusin can trigger on every click
        }

      }

      function onchange () {
        document.body.className = this[hidden] ? "hidden" : "visible";
        update_page();
      }

      function update_page(){
        if(windowFocused&&(document.body.className=="visible")){
          set_refresh_page(1000);
        }
      }


  })();
  set_refresh_page();
})

function get_date_time_string(){
  var d = new Date();
  var dT = [];
  dT.push(d.getDate());
  dT.push(d.getMonth())
  dT.push(d.getFullYear());
  dT.push(d.getHours());
  dT.push(d.getMinutes());
  dT.push(d.getSeconds());
  dT.push(d.getMilliseconds());
  return dT.join('_');
}

function do_refresh_page(){

// do tasks here

// e.g. some ajax call to update part of the page.

// (date time parameter will probably force the server not to cache)

//      $.ajax({
//        type: "POST",
//        url: "someUrl.php",
//        data: "t=" + get_date_time_string()+"&task=update",
//        success: function(html){
//          $('#content').html(html);
//        }
//      });

}

function set_refresh_page(interval){
  interval = typeof interval !== 'undefined' ? interval : 30000; // default time = 30 seconds
  if(timeOut2 != null) clearTimeout(timeOut2);
  timeOut2 = setTimeout(function(){
    if((document.body.className=="visible")&&windowFocused){
      do_refresh_page();
    }
    set_refresh_page();
  }, interval);
}

Dựa vào các phương pháp lấy nét / làm mờ không hoạt động (nó mang lại cho bạn rất nhiều kết quả dương tính giả), xem stackoverflow.com/a/9502074/698168
Julien Kronegg

2

Để biết giải pháp mà không cần jQuery, hãy xem Visibility.js cung cấp thông tin về ba trạng thái trang

visible    ... page is visible
hidden     ... page is not visible
prerender  ... page is being prerendered by the browser

và cũng đóng gói tiện lợi cho setInterval

/* Perform action every second if visible */
Visibility.every(1000, function () {
    action();
});

/* Perform action every second if visible, every 60 sec if not visible */
Visibility.every(1000, 60*1000, function () {
    action();
});

Dự phòng cho các trình duyệt cũ hơn (IE <10; iOS <7) cũng có sẵn


Hỗ trợ trình duyệt thì sao? bây giờ rất tốt trong chrome, safari và firefox.
Selva Ganapathi

1

Một cách phức tạp hơn một chút sẽ là sử dụng setInterval() để kiểm tra vị trí chuột và so sánh với lần kiểm tra cuối cùng. Nếu chuột không di chuyển trong một khoảng thời gian nhất định, người dùng có thể không sử dụng.

Điều này có thêm lợi thế là cho biết người dùng có rảnh không, thay vì chỉ kiểm tra xem cửa sổ không hoạt động.

Như nhiều người đã chỉ ra, đây không phải lúc nào cũng là một cách tốt để kiểm tra xem cửa sổ trình duyệt hay người dùng có ở chế độ chờ hay không, vì người dùng thậm chí có thể không sử dụng chuột hoặc đang xem video hoặc tương tự. Tôi chỉ đề xuất một cách có thể để kiểm tra sự nhàn rỗi.


30
Trừ khi người dùng không có chuột.
dùng1686

@Annan: Nó codinghorror.com/blog/2007/03/... bây giờ.
chiborg

Điều này cũng không phát xúc xắc nếu người dùng đang xem video
jamiew

bạn có thể sử dụng onkeypress hoặc các sự kiện tương tự khác để đặt lại bộ hẹn giờ và giải quyết vấn đề không phải chuột. Tất nhiên, nó vẫn không hoạt động đối với người dùng tích cực nhìn vào trang để xem video, nghiên cứu hình ảnh, v.v.
joshuahedlund

1

Đối với angular.js, đây là một lệnh (dựa trên câu trả lời được chấp nhận) sẽ cho phép bộ điều khiển của bạn phản ứng với sự thay đổi về khả năng hiển thị:

myApp.directive('reactOnWindowFocus', function($parse) {
    return {
        restrict: "A",
        link: function(scope, element, attrs) {
            var hidden = "hidden";
            var currentlyVisible = true;
            var functionOrExpression = $parse(attrs.reactOnWindowFocus);

          // Standards:
          if (hidden in document)
            document.addEventListener("visibilitychange", onchange);
          else if ((hidden = "mozHidden") in document)
            document.addEventListener("mozvisibilitychange", onchange);
          else if ((hidden = "webkitHidden") in document)
            document.addEventListener("webkitvisibilitychange", onchange);
          else if ((hidden = "msHidden") in document)
            document.addEventListener("msvisibilitychange", onchange);
          else if ("onfocusin" in document) {
                // IE 9 and lower:
            document.onfocusin = onshow;
                document.onfocusout = onhide;
          } else {
                // All others:
            window.onpageshow = window.onfocus = onshow;
                window.onpagehide = window.onblur = onhide;
            }

          function onchange (evt) {
                //occurs both on leaving and on returning
                currentlyVisible = !currentlyVisible;
                doSomethingIfAppropriate();
          }

            function onshow(evt) {
                //for older browsers
                currentlyVisible = true;
                doSomethingIfAppropriate();
            }

            function onhide(evt) {
                //for older browsers
                currentlyVisible = false;
                doSomethingIfAppropriate();
            }

            function doSomethingIfAppropriate() {
                if (currentlyVisible) {
                    //trigger angular digest cycle in this scope
                    scope.$apply(function() {
                        functionOrExpression(scope);
                    });
                }
            }
        }
    };

});

Bạn có thể sử dụng nó như ví dụ này : <div react-on-window-focus="refresh()">, trong đó refresh()một hàm phạm vi trong phạm vi của bất kỳ Trình điều khiển nào nằm trong phạm vi.


0

Đây là một giải pháp vững chắc, hiện đại. (Ngắn một ngọt ngào 👌🏽)

document.addEventListener("visibilitychange", () => {
  console.log( document.hasFocus() )
})

Điều này sẽ thiết lập một trình nghe để kích hoạt khi bất kỳ sự kiện hiển thị nào được kích hoạt có thể là tiêu điểm hoặc mờ.


0

Nếu bạn muốn hành động trên toàn bộ trình duyệt mờ : Như tôi đã nhận xét, nếu trình duyệt mất tập trung, không có sự kiện nào được đề xuất kích hoạt. Ý tưởng của tôi là đếm ngược trong một vòng lặp và đặt lại bộ đếm nếu xảy ra sự kiện. Nếu bộ đếm đạt đến giới hạn, tôi sẽ chuyển vị trí sang một trang khác. Điều này cũng kích hoạt nếu bạn làm việc trên các công cụ dev.

var iput=document.getElementById("hiddenInput");
   ,count=1
   ;
function check(){
         count++;
         if(count%2===0){
           iput.focus();
         }
         else{
           iput.blur();
         }
         iput.value=count;  
         if(count>3){
           location.href="http://Nirwana.com";
         }              
         setTimeout(function(){check()},1000);
}   
iput.onblur=function(){count=1}
iput.onfocus=function(){count=1}
check();

Đây là một dự thảo được thử nghiệm thành công trên FF.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.