มีวิธีการตรวจสอบหรือไม่ว่าหน้าต่างเบราว์เซอร์ไม่ทำงานในขณะนี้?


585

ฉันมีจาวาสคริปต์ที่ทำกิจกรรมเป็นระยะ เมื่อผู้ใช้ไม่ได้ดูเว็บไซต์ (เช่นหน้าต่างหรือแท็บไม่มีโฟกัส) มันก็ดีที่จะไม่ทำงาน

มีวิธีการทำเช่นนี้โดยใช้ JavaScript?

จุดอ้างอิงของฉัน: แชทของ Gmail เล่นเสียงหากหน้าต่างที่คุณใช้ไม่ได้ทำงานอยู่


8
สำหรับผู้ที่ไม่พึงพอใจกับคำตอบด้านล่างให้ตรวจสอบrequestAnimationFrameAPI หรือใช้คุณสมบัติที่ทันสมัยซึ่งความถี่ของsetTimeout/ setIntervalจะลดลงเมื่อมองไม่เห็นหน้าต่าง (เช่น 1 วินาทีใน Chrome เป็นต้น)
Rob W

2
document.body.onblur = function (e) {console.log ('lama');} ทำงานสำหรับองค์ประกอบที่ไม่ได้มุ่งเน้น
WhyMe

2
ดูคำตอบนี้สำหรับโซลูชันที่เข้ากันได้ข้ามเบราว์เซอร์ที่ใช้ W3C Page Visibility API กลับสู่blur/ focusในเบราว์เซอร์ที่ไม่รองรับ
งัด Bynens

2
80% ของคำตอบด้านล่างไม่ใช่คำตอบสำหรับคำถามนี้ คำถามถามเกี่ยวกับไม่ได้ใช้งานในปัจจุบันแต่มีคำตอบมากมายที่ด้านล่างไม่สามารถมองเห็นได้ซึ่งไม่ใช่คำตอบสำหรับคำถามนี้ ควรตั้งค่าสถานะเนื้อหาว่า "ไม่ใช่คำตอบ"
gman

คำตอบ:


691

ตั้งแต่เริ่มต้นเขียนคำตอบนี้สเปคใหม่ได้ถึงสถานะการแนะนำด้วย W3C หน้า API การเปิดเผย (เมื่อMDN ) ตอนนี้ช่วยให้เราสามารถตรวจสอบได้แม่นยำมากขึ้นเมื่อเพจถูกซ่อนอยู่ให้กับผู้ใช้

document.addEventListener("visibilitychange", onchange);

การสนับสนุนเบราว์เซอร์ปัจจุบัน:

รหัสต่อไปนี้กลับไปสู่วิธีเบลอ / โฟกัสที่เชื่อถือได้น้อยลงในเบราว์เซอร์ที่เข้ากันไม่ได้

(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"});
})();

onfocusinและonfocusoutจะต้องสำหรับ IE 9 และลดในขณะที่คนอื่น ๆ ทั้งหมดทำให้การใช้งานonfocusและการonblurยกเว้นสำหรับ iOS ซึ่งการใช้งานและonpageshowonpagehide


1
@ Bellpeace: IE ควรเผยแพร่focusinและfocusoutจาก iframe ไปที่หน้าต่างด้านบน สำหรับเบราว์เซอร์ที่ใหม่กว่าคุณจะต้องจัดการfocusและblurเหตุการณ์ในแต่ละwindowวัตถุของ iframe คุณควรใช้รหัสที่อัปเดตที่ฉันเพิ่งเพิ่มซึ่งอย่างน้อยจะครอบคลุมกรณีเหล่านั้นในเบราว์เซอร์ที่ใหม่กว่า
Andy E

3
@JulienKronegg: นั่นเป็นสาเหตุที่คำตอบของฉันกล่าวถึง Page Visibility API ซึ่งเข้าสู่สถานะฉบับร่างการทำงานหลังจากที่ฉันเขียนคำตอบของฉัน วิธีการโฟกัส / เบลอมีฟังก์ชันที่ จำกัด สำหรับเบราว์เซอร์รุ่นเก่า การผูกกับเหตุการณ์อื่น ๆ ดังที่คุณตอบไม่ได้ครอบคลุมมากกว่านี้และมีความเสี่ยงต่อความแตกต่างของพฤติกรรม (เช่น IE ไม่ได้ยิงเม้าส์เมื่อหน้าต่างปรากฏขึ้นใต้เคอร์เซอร์) ฉันขอแนะนำให้ดำเนินการที่เหมาะสมยิ่งขึ้นคือแสดงข้อความหรือไอคอนที่ระบุถึงผู้ใช้ว่าการอัพเดตอาจไม่บ่อยนักเนื่องจากหน้าเว็บไม่ทำงาน
Andy E

6
@ Andy ฉันลองใช้วิธีแก้ปัญหานี้กับโครเมียม มันทำงานได้ถ้าฉันเปลี่ยนแท็บ แต่มันไม่ได้ถ้าฉันเปลี่ยนหน้าต่าง (แท็บ ALT +) ควรเป็น? นี่คือซอ - jsfiddle.net/8a9N6/17
Tony Lâmpada

2
@Heliodor: ฉันต้องการให้รหัสในคำตอบน้อยที่สุดตอนนี้ มันไม่เคยตั้งใจที่จะเป็นโซลูชันที่สมบูรณ์แบบแบบตัดและวางเนื่องจากผู้ดำเนินการอาจต้องการหลีกเลี่ยงการตั้งค่าคลาสบนร่างกายและดำเนินการที่แตกต่างไปจากเดิมอย่างสิ้นเชิง (เช่นการหยุดและเริ่มจับเวลา)
Andy E

8
@AndyE ดูเหมือนว่าโซลูชันของคุณจะทำงานเฉพาะเมื่อผู้ใช้เปลี่ยนแท็บหรือย่อเล็กสุด / ขยายหน้าต่างให้ใหญ่สุด อย่างไรก็ตามเหตุการณ์ onchange จะไม่ถูกเรียกใช้หากผู้ใช้ออกจากแท็บที่ใช้งานอยู่ แต่จะเพิ่มโปรแกรมอื่นให้อยู่เหนือสุดจากทาสก์บาร์ มีวิธีแก้ปัญหาสำหรับสถานการณ์นั้นหรือไม่? ขอบคุณ!
user1491636

132

ฉันจะใช้ jQuery เพราะสิ่งที่คุณต้องทำคือ:

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

หรืออย่างน้อยก็ใช้งานได้สำหรับฉัน


1
สำหรับฉันการโทรครั้งนี้สองครั้งใน iframe
msangel

ใน Firefox หากคุณคลิกภายในคอนโซล firebug (ในหน้าเดียวกัน) windowโฟกัสจะหลวมซึ่งเหมาะสม แต่ขึ้นอยู่กับความตั้งใจของคุณที่อาจไม่ใช่สิ่งที่คุณต้องการ
Majid Fouladpour

21
สิ่งนี้ใช้ไม่ได้กับเบราว์เซอร์รุ่นใหม่ในปัจจุบันดูคำตอบที่ได้รับอนุมัติ (Page Visibility API)
Jon z

โซลูชันนี้ใช้งานไม่ได้กับ iPad โปรดใช้เหตุการณ์ "pageshow"
ElizaS

ทั้ง BLUR และ FOCUS จะดับเมื่อโหลดหน้าเว็บ เมื่อฉันเปิดหน้าต่างใหม่จากหน้าของฉันไม่มีอะไรเกิดขึ้น แต่เมื่อหน้าต่างใหม่ปิดทั้งสองเหตุการณ์ก็ปิดลง: / (โดยใช้ IE8)
SearchForKnowledge

49

มีวิธีการทั่วไป 3 วิธีที่ใช้ในการพิจารณาว่าผู้ใช้สามารถดูหน้า HTML ได้หรือไม่ แต่ไม่มีวิธีการใดที่สมบูรณ์แบบ:

  • W3C หน้า API การเปิดเผยควรจะทำเช่นนี้ (ได้รับการสนับสนุนตั้งแต่: Firefox 10 MSIE 10, Chrome 13) อย่างไรก็ตาม API นี้จะเพิ่มเหตุการณ์เฉพาะเมื่อแท็บเบราว์เซอร์นั้นเต็มไปหมด (เช่นเมื่อผู้ใช้เปลี่ยนจากแท็บหนึ่งเป็นอีกแท็บหนึ่ง) API จะไม่เพิ่มเหตุการณ์เมื่อไม่สามารถระบุการมองเห็นได้ด้วยความแม่นยำ 100% (เช่น Alt + Tab เพื่อเปลี่ยนเป็นแอปพลิเคชันอื่น)

  • การใช้วิธีการโฟกัส / เบลอจะให้ผลบวกที่ผิด ๆ มากมาย ตัวอย่างเช่นหากผู้ใช้แสดงหน้าต่างเล็ก ๆ ที่ด้านบนของหน้าต่างเบราว์เซอร์หน้าต่างเบราว์เซอร์จะสูญเสียการโฟกัส ( onblurยกระดับขึ้น) แต่ผู้ใช้ยังสามารถมองเห็นได้ (ดังนั้นจึงจำเป็นต้องรีเฟรช) ดูเพิ่มเติมที่http://javascript.info/tutorial/focus

  • การใช้กิจกรรมของผู้ใช้ (การเลื่อนเมาส์คลิกการพิมพ์คีย์) จะให้ผลบวกที่ผิด ๆ มากมาย นึกถึงกรณีเดียวกับด้านบนหรือผู้ใช้ที่ดูวิดีโอ

เพื่อปรับปรุงพฤติกรรมที่ไม่สมบูรณ์ที่อธิบายไว้ข้างต้นฉันใช้การรวมกันของ 3 วิธี: W3C Visibility API จากนั้นทำการโฟกัส / เบลอและวิธีการใช้งานของผู้ใช้เพื่อลดอัตราบวกผิด ๆ สิ่งนี้อนุญาตให้จัดการเหตุการณ์ต่อไปนี้:

  • การเปลี่ยนแท็บเบราว์เซอร์ไปเป็นอีกอันหนึ่ง (ความแม่นยำ 100% ขอบคุณ W3C Page Visibility API)
  • หน้าอาจซ่อนโดยหน้าต่างอื่นเช่นเนื่องจาก Alt + Tab (probabilistic = ไม่ถูกต้อง 100%)
  • ความสนใจของผู้ใช้อาจไม่ได้เน้นที่หน้า HTML (ความน่าจะเป็น = ไม่ถูกต้อง 100%)

นี่คือวิธีการทำงาน: เมื่อเอกสารสูญเสียการโฟกัสกิจกรรมของผู้ใช้ (เช่นการเลื่อนเมาส์) บนเอกสารจะถูกตรวจสอบเพื่อตรวจสอบว่าสามารถมองเห็นหน้าต่างได้หรือไม่ ความน่าจะเป็นในการเปิดเผยหน้านั้นจะแปรผกผันกับเวลาของกิจกรรมผู้ใช้ล่าสุดในหน้าเว็บ: หากผู้ใช้ไม่ได้ทำกิจกรรมบนเอกสารเป็นเวลานานหน้านั้นอาจไม่สามารถมองเห็นได้ โค้ดด้านล่างเลียนแบบ W3C Page Visibility API: มันทำงานในลักษณะเดียวกัน แต่มีอัตราบวกที่ผิดพลาดเล็กน้อย มันมีข้อดีที่จะเป็น multibrowser (ทดสอบบน Firefox 5, Firefox 10, MSIE 9, MSIE 7, Safari 5, Chrome 9)

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

    <script>
    / **
    ลงทะเบียนตัวจัดการเหตุการณ์สำหรับวัตถุที่กำหนด
    @param obj วัตถุที่จะเพิ่มเหตุการณ์
    @param evType ประเภทเหตุการณ์: คลิก, ปุ่มกด, เม้าส์, ...
    @param fn ฟังก์ชันตัวจัดการเหตุการณ์
    @param isCapturing ตั้งค่าโหมดเหตุการณ์ (true = เหตุการณ์การจับภาพ, false = เหตุการณ์เดือดปุด ๆ )
    @return จริงหากตัวจัดการเหตุการณ์ได้รับการแนบอย่างถูกต้อง
    * /
    ฟังก์ชั่น addEvent (obj, evType, fn, isCapturing) {
      if (isCapturing == null) isCapturing = false; 
      if (obj.addEventListener) {
        // Firefox
        obj.addEventListener (evType, fn, isCapturing);
        กลับจริง
      } อื่น ๆ ถ้า (obj.attachEvent) {
        // MSIE
        var r = obj.attachEvent ('บน' + evType, fn);
        กลับ r;
      } อื่น {
        กลับเท็จ
      }
    }

    // ลงทะเบียนเพื่อเปลี่ยนการเปิดเผยหน้าเว็บที่เป็นไปได้
    addEvent (เอกสาร, "potentialvisilitychange", ฟังก์ชัน (เหตุการณ์) {
      document.getElementById ("x"). InnerHTML + = "potentialVisilityChange: potentialHidden =" + document.potentialHidden + ", document.potentialHiddenSince =" + document.potentialHiddenSince + "s" ";
    });

    // ลงทะเบียนกับ W3C Page Visibility API
    var hidden = null;
    var visibleChange = null;
    if (typeof document.mozHidden! == "undefined") {
      ซ่อน = "mozHidden";
      visibilityChange = "mozvisibilitychange";
    } อื่นถ้า (typeof document.msHidden! == "undefined") {
      ซ่อน = "msHidden";
      visibilityChange = "msvisibilitychange";
    } อื่นถ้า (typeof document.webkitHidden! == "ไม่ได้กำหนด") {
      ซ่อน = "webkitHidden";
      visibilityChange = "webkitvisibilitychange";
    } อื่นถ้า (typeof document.hidden! == "ซ่อน") {
      ซ่อน = "ซ่อน";
      visibilityChange = "visibilitychange";
    }
    if (hidden! = null && visibleChange! = null) {
      addEvent (เอกสาร, ทัศนวิสัยเปลี่ยน, ฟังก์ชั่น (เหตุการณ์) {
        document.getElementById ("x"). InnerHTML + = visibleChange + ":" + hidden + "=" + เอกสาร [ซ่อน] + "<br>";
      });
    }


    var potentialPageVisibility = {
      pageVisibilityChangeThreshold: 3 * 3600, // ในไม่กี่วินาที
      init: function () {
        ฟังก์ชัน setAsNotHidden () {
          var dispatchEventRequired = document.potentialHidden;
          document.potentialHidden = false;
          document.potentiallyHiddenSince = 0;
          ถ้า (dispatchEventRequired) dispatchPageVisibilityChangeEvent ();
        }

        ฟังก์ชัน initPotentiallyHiddenDetection () {
          if (! hasFocusLocal) {
            // หน้าต่างไม่มี focus => ตรวจสอบกิจกรรมของผู้ใช้ในหน้าต่าง
            lastActionDate = new Date ();
            if (timeoutHandler! = null) {
              clearTimeout (timeoutHandler);
            }
            timeoutHandler = setTimeout (checkPageVisibility, potentialPageVisibility.pageVisibilityChangeThreshold * 1000 + 100); // +100 ms เพื่อหลีกเลี่ยงปัญหาการปัดเศษภายใต้ Firefox
          }
        }

        ฟังก์ชั่น dispatchPageVisibilityChangeEvent () {
          unifiedVisilityChangeEventDispatchAllowed = false;
          var evt = document.createEvent ("เหตุการณ์");
          evt.initEvent ("potentialvisilitychange", จริง, จริง);
          document.dispatchEvent (EVT);
        }

        ฟังก์ชัน checkPageVisibility () {
          var potentialHiddenDuration = (hasFocusLocal || lastActionDate == null? 0: Math.floor ((วันที่ใหม่ (). getTime () - lastActionDate.getTime ()) / 1000);
                                        document.potentiallyHiddenSince = potentialHiddenDuration;
          if (potentialHiddenDuration> = potentialPageVisibility.pageVisibilityChangeThreshold &&! document.potentialHidden) {
            // หน้าเปลี่ยนเกณฑ์การมองเห็น raiched => ยกระดับ
            document.potentialHidden = true;
            dispatchPageVisibilityChangeEvent ();
          }
        }

        var lastActionDate = null;
        var hasFocusLocal = true;
        var hasMouseOver = true;
        document.potentialHidden = false;
        document.potentiallyHiddenSince = 0;
        var timeoutHandler = null;

        addEvent (เอกสาร, "pageshow", ฟังก์ชัน (เหตุการณ์) {
          document.getElementById ( "x") innerHTML + = "pageshow / doc: <br>".
        });
        addEvent (เอกสาร, "pagehide", ฟังก์ชัน (เหตุการณ์) {
          document.getElementById ( "x") innerHTML + = "pagehide / doc: <br>".
        });
        addEvent (หน้าต่าง "pageshow", ฟังก์ชัน (เหตุการณ์) {
          document.getElementById ( "x") innerHTML + = "pageshow / ชนะ: <br>". // เพิ่มเมื่อหน้าแรกแสดง
        });
        addEvent (หน้าต่าง "pagehide", ฟังก์ชัน (เหตุการณ์) {
          document.getElementById ( "x") innerHTML + = "pagehide / ชนะ: <br>". // ไม่ยกขึ้น
        });
        addEvent (เอกสาร "mousemove", ฟังก์ชัน (เหตุการณ์) {
          lastActionDate = new Date ();
        });
        addEvent (เอกสาร "mouseover", ฟังก์ชัน (เหตุการณ์) {
          hasMouseOver = true;
          setAsNotHidden ();
        });
        addEvent (เอกสาร "mouseout", function (event) {
          hasMouseOver = false;
          initPotentiallyHiddenDetection ();
        });
        addEvent (หน้าต่าง "เบลอ" ฟังก์ชั่น (เหตุการณ์) {
          hasFocusLocal = false;
          initPotentiallyHiddenDetection ();
        });
        addEvent (หน้าต่าง, "โฟกัส", ฟังก์ชัน (เหตุการณ์) {
          hasFocusLocal = true;
          setAsNotHidden ();
        });
        setAsNotHidden ();
      }
    }

    potentialPageVisibility.pageVisibilityChangeThreshold = 4; // 4 วินาทีสำหรับการทดสอบ
    potentialPageVisibility.init ();
    </ script>

เนื่องจากขณะนี้ยังไม่มีโซลูชันข้ามเบราว์เซอร์ที่ใช้งานได้โดยไม่มีข้อผิดพลาดในเชิงบวกคุณควรคิดให้ดีขึ้นสองครั้งเกี่ยวกับการปิดใช้งานกิจกรรมเป็นระยะ ๆ บนเว็บไซต์ของคุณ


จะไม่ใช้โอเปอเรเตอร์การเปรียบเทียบที่เข้มงวดกับสตริง 'ไม่ได้กำหนด' แทนที่จะเป็นคำหลักที่ไม่ได้กำหนดทำให้เกิดผลบวกปลอมในรหัสข้างต้นหรือไม่
Jonathon

@kiran: อันที่จริงมันทำงานร่วมกับ Alt + Tab คุณไม่สามารถระบุได้ว่าหน้านั้นถูกซ่อนไว้เมื่อคุณใช้ Alt + Tab หรือไม่เพราะคุณอาจเปลี่ยนเป็นหน้าต่างที่เล็กกว่าดังนั้นคุณจึงไม่สามารถรับประกันได้ว่าหน้าของคุณจะถูกซ่อนไว้อย่างสมบูรณ์ นี่คือเหตุผลที่ฉันใช้ความคิดของ "ซ่อนเร้น" (ในตัวอย่างขีด จำกัด ถูกตั้งค่าเป็น 4 วินาทีดังนั้นคุณต้องสลับไปที่หน้าต่างอื่นโดยใช้ Alt + Tab เป็นเวลาอย่างน้อย 4 วินาที) อย่างไรก็ตามความคิดเห็นของคุณแสดงให้เห็นว่าคำตอบนั้นไม่ชัดเจนดังนั้นฉันจึงใส่คำตอบใหม่
Julien Kronegg

@JulienKronegg ฉันคิดว่านี่เป็นทางออกที่ดีที่สุด อย่างไรก็ตามรหัสข้างต้นจำเป็นต้องมีการรีแฟคเตอร์และบทคัดย่อ ทำไมคุณไม่อัปโหลดไปยัง GitHub และให้ชุมชนทำการปรับโครงสร้างใหม่
จาค็อบ

1
@Jacob ฉันดีใจที่คุณชอบทางออกของฉัน อย่าลังเลที่จะโปรโมตให้เป็นโครงการ GitHub ด้วยตัวเอง ฉันให้รหัสด้วยสัญญาอนุญาตครีเอทีฟคอมมอนส์โดยcreativecommons.org/licenses/by/4.0
Julien Kronegg

26

มีห้องสมุดเรียบร้อยที่ GitHub:

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

ตัวอย่าง:

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

ฉันได้ทดสอบเวอร์ชัน 1.0.1 บนเบราว์เซอร์ทั้งหมดที่ฉันมีและสามารถยืนยันได้ว่าทำงานได้กับ:

  • IE9, IE10
  • FF 26.0
  • Chrome 34.0

... และอาจเป็นเวอร์ชั่นที่ใหม่กว่าทั้งหมด

ใช้งานไม่ได้กับ:

  • IE8 - ระบุว่าแท็บ / หน้าต่างนั้นทำงานอยู่.now()เสมอ( กลับมาเสมอtrueสำหรับฉัน)

คำตอบที่ยอมรับทำให้เกิดปัญหาใน IE9 ห้องสมุดนี้ใช้งานได้ดี
Tom Teman

20

การใช้: API การเปิดเผยหน้าเว็บ

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

ฉันสามารถใช้ ? http://caniuse.com/#feat=pagevisibility


คำถามไม่ได้เกี่ยวกับการเปิดเผยหน้า มันเกี่ยวกับไม่ได้ใช้งาน / ใช้งาน
gman

ฉันคิดว่า OP ไม่ได้พูดถึงฟังก์ชั่นของ ide
l2aelba

1
ฉันไม่ได้พูดถึง ide ของเช่นกัน ฉันกำลังพูดถึง alt-tabbing / cmd-tabbing กับแอปอื่น ทันใดนั้นหน้าไม่ได้ใช้งาน api การมองเห็นหน้าไม่ช่วยให้ฉันรู้ว่าถ้าหน้าไม่ได้ใช้งานมันจะช่วยให้ฉันรู้ว่าอาจจะไม่สามารถมองเห็นได้
gman

18

ฉันสร้าง Comet Chat สำหรับแอปของฉันและเมื่อฉันได้รับข้อความจากผู้ใช้รายอื่นฉันใช้:

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

2
ทางออกที่สะอาดที่สุดพร้อมการสนับสนุนกลับไปที่ IE6
Paul Cooper

4
document.hasFocus()เป็นวิธีที่สะอาดที่สุดที่จะทำ วิธีอื่น ๆ ทั้งหมดที่ใช้ api การมองเห็นหรือตามเหตุการณ์หรือมองหากิจกรรมของผู้ใช้ในระดับต่างๆ / การขาดกิจกรรมกลายเป็นเรื่องที่ซับซ้อนและเต็มไปด้วยกรณีและช่องโหว่ ใส่มันในช่วงเวลาที่เรียบง่ายและยกระดับเหตุการณ์ที่กำหนดเองเมื่อผลการเปลี่ยนแปลง ตัวอย่าง: jsfiddle.net/59utucz6/1
danatcofo

1
มีประสิทธิภาพและแตกต่างจากโซลูชันอื่น ๆ ที่ให้ข้อเสนอแนะที่ถูกต้องเมื่อคุณสลับไปที่แท็บหรือหน้าต่างเบราว์เซอร์อื่น
ow3n

ไม่ต้องสงสัยเลยว่ามันเป็นวิธีที่สะอาดที่สุด แต่มันไม่ทำงานใน firefox
hardik chugh

1
หากฉันเปิดเครื่องมือ Chrome Dev แล้ว document.hasFocus () เท่ากับเท็จ หรือแม้ว่าคุณจะคลิกที่แผงด้านบนของเบราว์เซอร์ก็จะเกิดขึ้นเช่นเดียวกัน ผมไม่แน่ใจว่าวิธีนี้เหมาะที่จะหยุดวิดีโอ, ภาพเคลื่อนไหว, ฯลฯ
tylik

16

ฉันเริ่มใช้คำตอบวิกิชุมชน แต่รู้ว่ามันไม่ได้ตรวจพบเหตุการณ์แท็บ alt ใน Chrome นี่เป็นเพราะมันใช้แหล่งเหตุการณ์แรกที่มีอยู่และในกรณีนี้คือ API การเปิดเผยหน้าเว็บซึ่งใน Chrome ดูเหมือนว่าจะไม่ติดตามการแท็บ alt

ฉันตัดสินใจที่จะปรับเปลี่ยนสคริปต์เล็กน้อยเพื่อติดตามเหตุการณ์ที่เป็นไปได้ทั้งหมดสำหรับการเปลี่ยนแปลงการโฟกัสหน้า นี่คือฟังก์ชั่นที่คุณสามารถดรอปได้:

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;
};

ใช้มันแบบนี้:

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

รุ่นนี้ฟังสำหรับทุกเหตุการณ์ที่เกิดขึ้นในการมองเห็นที่แตกต่างกันและไฟโทรกลับถ้าใด ๆ ของพวกเขาทำให้เกิดการเปลี่ยนแปลง ตัวจัดการfocusedและunfocusedตรวจสอบให้แน่ใจว่าการเรียกกลับไม่ได้ถูกเรียกหลายครั้งถ้า API หลายตัวเปลี่ยนแปลงการมองเห็นที่เหมือนกัน


Chrome สำหรับตัวอย่างเช่นมีทั้งและdocument.hidden document.webkitHiddenโดยไม่ต้องelseในifการก่อสร้างที่เราจะได้รับ 2 สายโทรกลับใช่มั้ย?
Christiaan Westerbeek

@ChristiaanWesterbeek นั่นเป็นจุดที่ดีฉันไม่ได้คิดอย่างนั้น! หากคุณสามารถแก้ไขโพสต์นี้ไปข้างหน้าและผมจะยอมรับ :)
แดเนียล Buckmaster

เอ๊ะเดี๋ยวก่อน: การแก้ไขเพื่อเพิ่ม "else" ที่แนะนำโดย ChristiaanWesterbeek และที่จริงแล้วเพิ่มโดย @ 1.21Gigawatts ดูเหมือนจะไม่เป็นความคิดที่ดี: มันเอาชนะการซื้อเดิมของความคิดของ Daniel ซึ่งจะลองทั้งหมดที่สนับสนุน วิธีการในแบบคู่ขนาน และไม่มีความเสี่ยงในการโทรกลับที่ถูกเรียกสองครั้งเพราะ focus () และ unfocused () ระงับการโทรพิเศษเมื่อไม่มีอะไรเปลี่ยนแปลง ดูเหมือนว่าจริง ๆ แล้วเราควรกลับไปใช้การแก้ไขครั้งแรก
Louis Semprini

@ LouisSemprini นั่นเป็นเรื่องที่ยอดเยี่ยมมาก ฉันลืมความตั้งใจดั้งเดิมของรหัส! ฉันกู้คืนต้นฉบับแล้วเพิ่มคำอธิบาย!
Daniel Buckmaster

ตรวจสอบสิ่งนี้ตั้งแต่วันนี้มันตรวจไม่พบแท็บ alt + อย่างน้อยบน Chrome 78 + macos
Hugo Gresse

7

นี่มันช่างยากจริงๆ ดูเหมือนจะไม่มีวิธีแก้ปัญหาตามข้อกำหนดดังต่อไปนี้

  • หน้านี้ประกอบด้วย iframes ที่คุณไม่สามารถควบคุมได้
  • คุณต้องการติดตามการเปลี่ยนแปลงสถานะการมองเห็นโดยไม่คำนึงถึงการเปลี่ยนแปลงที่เกิดขึ้นจากการเปลี่ยนแปลง TAB (แท็บ ctrl +) หรือการเปลี่ยนแปลงหน้าต่าง (แท็บ alt +)

สิ่งนี้เกิดขึ้นเพราะ:

  • API การมองเห็นของหน้าสามารถบอกคุณได้อย่างน่าเชื่อถือถึงการเปลี่ยนแปลงแท็บ (แม้จะมี iframe) แต่ก็ไม่สามารถบอกคุณได้เมื่อผู้ใช้เปลี่ยนหน้าต่าง
  • การฟังเหตุการณ์หน้าต่างเบลอ / โฟกัสสามารถตรวจจับ alt + tab และ ctrl + tab ได้ตราบใดที่ iframe ไม่มีโฟกัส

ด้วยข้อ จำกัด เหล่านี้จึงเป็นไปได้ที่จะใช้โซลูชันที่รวม - API การมองเห็นหน้า - การเบลอของหน้าต่าง / โฟกัส - document.activeElement

ที่สามารถ:

  • 1) แท็บ ctrl + เมื่อเพจระดับบนมีโฟกัส: ใช่
  • 2) แท็บ ctrl + เมื่อ iframe มีโฟกัส: ใช่
  • 3) แท็บ alt + เมื่อเพจระดับบนมีโฟกัส: ใช่
  • 4) alt + tab เมื่อ iframe มีโฟกัส: NO <- คนเกียจคร้าน

เมื่อ iframe มีโฟกัสเหตุการณ์เบลอ / โฟกัสของคุณจะไม่ถูกเรียกใช้เลยและ API การมองเห็นหน้าเว็บจะไม่เรียกใช้แท็บ alt +

ฉันสร้างโซลูชันของ @ AndyE และใช้โซลูชัน (เกือบดี) ที่นี่: https://dl.dropboxusercontent.com/u/2683925/estante-components/visibility_test1.html (ขออภัยฉันมีปัญหากับ JSFiddle)

นอกจากนี้ยังมีให้ที่ Github: https://github.com/qmagico/estante-components

ใช้ได้กับโครเมียม / โครเมียม มันใช้งานได้กับ firefox ยกเว้นว่าจะไม่โหลดเนื้อหา iframe (คิดว่าเพราะอะไร)

อย่างไรก็ตามเพื่อแก้ไขปัญหาล่าสุด (4) วิธีเดียวที่คุณสามารถทำได้คือฟังเหตุการณ์เบลอ / โฟกัสบน iframe หากคุณมีสิทธิ์ควบคุม iframes คุณสามารถใช้ postMessage API เพื่อทำสิ่งนั้น

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

ฉันยังไม่ได้ทดสอบกับเบราว์เซอร์เพียงพอ หากคุณสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับสถานที่ที่ใช้งานไม่ได้โปรดแจ้งให้เราทราบในความคิดเห็นด้านล่าง


ในการทดสอบของฉันมันยังทำงานกับ IE9, IE10 และ Chrome บน Android
Tony Lâmpada

1
ดูเหมือนว่า IPAD ต้องการโซลูชันที่แตกต่างอย่างสิ้นเชิง - stackoverflow.com/questions/4940657/ …
Tony Lâmpada

3
ลิงก์ทั้งหมดเหล่านี้เป็น 404 วินาที :(
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/



3

คุณสามารถใช้:

(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

ใน HTML 5 คุณสามารถใช้:

  • onpageshow: สคริปต์ที่จะทำงานเมื่อหน้าต่างมองเห็นได้
  • onpagehide: สคริปต์ที่จะทำงานเมื่อซ่อนหน้าต่าง

ดู:


2
ฉันคิดว่ามันเกี่ยวข้องกับ BFCache: เมื่อผู้ใช้คลิก Back หรือ Forward - มันไม่เกี่ยวข้องกับหน้าที่อยู่ที่ด้านบนสุดของเดสก์ท็อปคอมพิวเตอร์
nonopolarity

2

นี่คือการปรับคำตอบจาก Andy E.

สิ่งนี้จะทำงานเช่นรีเฟรชหน้าทุก ๆ 30 วินาที แต่ก็ต่อเมื่อหน้านั้นสามารถมองเห็นและโฟกัสได้

หากไม่สามารถมองเห็นทัศนวิสัยได้จะใช้เฉพาะการโฟกัสเท่านั้น

หากผู้ใช้มุ่งเน้นหน้าแล้วมันจะอัปเดตทันที

หน้าจะไม่อัปเดตอีกครั้งจนถึง 30 วินาทีหลังจากมีการโทร ajax

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);
}

การใช้วิธีการโฟกัส / เบลอไม่ได้ผล (มันให้ผลบวกปลอมมากมาย) ดู stackoverflow.com/a/9502074/698168
Julien Kronegg

2

สำหรับโซลูชันที่ไม่มี jQuery ให้ตรวจสอบVisibility.jsซึ่งให้ข้อมูลเกี่ยวกับสถานะหน้าสามหน้า

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

และตัวห่อหุ้มสิ่งอำนวยความสะดวกสำหรับ 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();
});

ทางเลือกสำหรับเบราว์เซอร์รุ่นเก่า (IE <10; iOS <7) ก็มีให้เช่นกัน


เบราว์เซอร์รองรับอะไรบ้าง ตอนนี้การตีดีใน chrome, Safari, และ firefox
Selva Ganapathi

1

วิธีที่ซับซ้อนกว่านี้เล็กน้อยคือใช้setInterval()เพื่อตรวจสอบตำแหน่งของเมาส์และเปรียบเทียบกับการตรวจสอบครั้งล่าสุด หากเมาส์ไม่ได้เคลื่อนที่ตามระยะเวลาที่กำหนดผู้ใช้อาจไม่ได้ทำงาน

นี้มีประโยชน์เพิ่มของการบอกหากผู้ใช้ไม่ได้ใช้งานแทนการเพียงแค่การตรวจสอบถ้าหน้าต่างไม่ได้ใช้งาน

ดังที่หลายคนชี้ว่านี่ไม่ใช่วิธีที่ดีในการตรวจสอบว่าผู้ใช้หรือหน้าต่างเบราว์เซอร์ไม่ได้ทำงานเนื่องจากผู้ใช้อาจไม่ได้ใช้เมาส์หรือดูวิดีโอหรือคล้ายกัน ฉันขอแนะนำวิธีหนึ่งที่เป็นไปได้ในการตรวจสอบความว่าง


30
นอกจากว่าผู้ใช้ไม่มีเม้าส์
1686

@Annan: มันเป็นcodinghorror.com/blog/2007/03/…ตอนนี้
chiborg

สิ่งนี้จะไม่เล่นลูกเต๋าหากผู้ใช้ดูวิดีโอด้วย
jamiew

คุณสามารถใช้ onkeypress หรือเหตุการณ์อื่นที่คล้ายคลึงกันเพื่อรีเซ็ตตัวจับเวลาและแก้ปัญหาที่ไม่ใช่เมาส์ แน่นอนว่ามันยังคงใช้งานไม่ได้สำหรับผู้ใช้ที่กำลังดูหน้าเพื่อดูวิดีโอศึกษารูปภาพ ฯลฯ
joshuahedlund

1

สำหรับ angular.js ต่อไปนี้เป็นคำสั่ง (ตามคำตอบที่ยอมรับ) ซึ่งจะทำให้คอนโทรลเลอร์ของคุณตอบสนองต่อการเปลี่ยนแปลงในการมองเห็น:

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);
                    });
                }
            }
        }
    };

});

คุณสามารถใช้มันเหมือนกับตัวอย่างนี้: <div react-on-window-focus="refresh()">โดยที่refresh()ฟังก์ชันขอบเขตในขอบเขตของตัวควบคุมใดก็ตามที่อยู่ในขอบเขต


0

นี่คือทางออกที่มั่นคงและทันสมัย (สั้นหวาน👌🏽)

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

การทำเช่นนี้จะเป็นการตั้งค่าให้ผู้ฟังเริ่มทำงานเมื่อมีเหตุการณ์การมองเห็นเกิดขึ้นซึ่งอาจเป็นจุดโฟกัสหรือเบลอ


0

หากคุณต้องการที่จะทำหน้าที่ในทั้งเบลอเบราว์เซอร์ : ขณะที่ผมให้ความเห็นว่าถ้าไม่มีเบราว์เซอร์มุ่งเน้นการสูญเสียในเหตุการณ์ไฟไหม้ปัญหา ความคิดของฉันคือการนับในลูปและรีเซ็ตตัวนับหากเหตุการณ์เริ่มทำงาน หากตัวนับถึงขีด จำกัด ฉันจะทำที่อยู่ให้คลิกไปยังหน้าอื่น สิ่งนี้จะเกิดขึ้นหากคุณทำงานกับเครื่องมือพัฒนา

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();

นี่เป็นร่างที่ผ่านการทดสอบกับ FF เรียบร้อยแล้ว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.