ผู้ฟังเหตุการณ์เมื่อมองเห็นองค์ประกอบ?


115

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

ขอบคุณ


คำตอบนี้ช่วยได้หรือไม่? stackoverflow.com/questions/1397251/…
kangax

@kangax ขอบคุณครับ. แต่เนื่องจากไม่ได้นำไปใช้อย่างกว้างขวางฉันคิดว่าฉันจะเกาแนวคิดของผู้ฟังเหตุการณ์ทั้งหมดและไปเส้นทางอื่น
JD Isaacks

ดูคำตอบสำหรับการใช้งานเหตุการณ์ "onVisible" ใน JavaScript: stackoverflow.com/a/3807340/975097
Anderson Green

เป็นไปได้ที่จะซ้ำกันของวิธีใช้เหตุการณ์ 'onVisible' ใน Javascript?
ผู้ใช้

กรุณาดูสิ่งนี้ stackoverflow.com/questions/45429746/…
Martin

คำตอบ:


8

เหตุการณ์Javascriptจัดการกับการโต้ตอบของผู้ใช้หากรหัสของคุณได้รับการจัดระเบียบเพียงพอคุณควรจะสามารถเรียกใช้ฟังก์ชันเริ่มต้นในตำแหน่งเดียวกับที่การมองเห็นเปลี่ยนไป (เช่นคุณไม่ควรเปลี่ยนmyElement.style.displayในหลาย ๆ ที่ให้เรียกใช้ฟังก์ชัน / วิธีการแทน สิ่งนี้และสิ่งอื่นที่คุณอาจต้องการ)


8
แต่คุณจะตรวจจับการเปลี่ยนแปลงในการมองเห็นได้อย่างไร (เช่นเรียกใช้ฟังก์ชันทันทีที่มองเห็นองค์ประกอบ)
Anderson Green

ฉันคิดว่าคุณทำอย่างนั้นไม่ได้ สิ่งที่คุณจะทำแทนที่จะพยายามตรวจจับการเปลี่ยนแปลงคือรู้ว่ามันถูกกระตุ้นตรงไหนและโทรไปที่นั่น หากการเปลี่ยนแปลงในการมองเห็นไม่ใช่สิ่งที่รหัสของคุณต้องรับผิดชอบโดยตรง (เช่น lib บางตัว) และตรรกะที่เกิดขึ้นนั้นมีการวางแผนไว้มากฉันเดาว่าคุณโชคไม่ดี? :)
maltalef

6
ฉันปฏิเสธคำถามนี้เพราะแม้จะมีความหมายทั้งหมด แต่ก็ไม่เกี่ยวข้องกับปัญหาที่เกิดขึ้นจริง (ถ้าฉันไม่แสดงบนเฟรมองค์ประกอบลูกทั้งหมดจะมองไม่เห็น)
Sebas

2
@ เซบาสฉันไม่เข้าใจจริงๆว่าทำไมการมองเห็นของเด็กจึงสำคัญที่นี่ อย่างไรก็ตามหากคำตอบไม่เป็นประโยชน์สำหรับคุณในบางกรณี (ไม่ว่าจะเป็นอะไรก็ตาม) แต่ชี้ไปที่วิธีแก้ปัญหา (หรือคำอธิบายว่าทำไมไม่มีทางแก้ปัญหา) สำหรับโปรแกรมเมอร์ส่วนใหญ่ก็ยังคงเป็นคำตอบที่ถูกต้องในความเห็นที่ต่ำต้อยของฉัน หากคุณชอบคำตอบอื่นที่ดีกว่า (โดยเฉพาะคำตอบที่เกิดขึ้นในเวลาต่อมาเมื่อเทคโนโลยีได้รับการปรับปรุงและมีตัวเลือกใหม่ ๆ ) ฉันเชื่อว่าคำขอให้เปลี่ยนคำตอบที่ถูกต้องน่าจะเหมาะสมกว่าการโหวตลดคะแนน ไม่ว่าในกรณีใดขอบคุณสำหรับการแก้ไข :)
maltalef

2
@figha ไม่ใช่เด็ก ๆ แต่ถ้าองค์ประกอบที่คุณกำลังดูอยู่ในเฟรมที่แสดง = ไม่มีคุณจะไม่มีสัญญาณใด ๆ
Sebas

67

ในอนาคต HTML Intersection Observer API ใหม่คือสิ่งที่คุณกำลังมองหา ช่วยให้คุณกำหนดค่าการเรียกกลับที่เรียกเมื่อใดก็ตามที่องค์ประกอบหนึ่งที่เรียกว่าเป้าหมายตัดกับวิวพอร์ตของอุปกรณ์หรือองค์ประกอบที่ระบุ มีให้บริการใน Chrome, Firefox และ Edge เวอร์ชันล่าสุด ดูhttps://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_APIสำหรับข้อมูลเพิ่มเติม

ตัวอย่างโค้ดง่ายๆสำหรับการสังเกตการแสดงผล: ไม่มีการสลับ:

// Start observing visbility of element. On change, the
//   the callback is called with Boolean visibility as
//   argument:

respondToVisibility(element, callback) {
  var options = {
    root: document.documentElement
  }

  var observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      callback(entry.intersectionRatio > 0);
    });
  }, options);

  observer.observe(element);
}

ในการดำเนินการ: https://jsfiddle.net/elmarj/u35tez5n/5/


2
> Intersection Observer API ช่วยให้คุณกำหนดค่าการเรียกกลับที่เรียกเมื่อใดก็ตามที่องค์ประกอบหนึ่งเรียกว่าเป้าหมายตัดระหว่างวิวพอร์ตของอุปกรณ์หรือองค์ประกอบที่ระบุ
จนถึง

1
โปรดทราบว่า InteractionObserver ไม่ทำงานบน Safari ในขณะนี้ caniuse.com/#feat=intarterobserverดูเหมือนว่ากำลังดำเนินการแก้ไข bugs.webkit.org/show_bug.cgi?id=159475
Pic Mickael

8
เมื่อใดที่ IE ขาดการสนับสนุนสำหรับอะไรที่เจ๋ง ๆ แบบนี้จะหยุดทำลายชีวิตเรา?
Peter Moore

5
@PeterMoore ทันทีที่คุณหยุดพยายามสนับสนุนมัน เพียงแค่ไม่
johny why

4
ฮ่า ๆ. บางครั้งมันไม่ใช่การตัดสินใจของเรา
Peter Moore

45
var targetNode = document.getElementById('elementId');
var observer = new MutationObserver(function(){
    if(targetNode.style.display != 'none'){
        // doSomething
    }
});
observer.observe(targetNode, { attributes: true, childList: true });

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


4
สิ่งนี้ใช้ไม่ได้เมื่อไม่แสดง targetNode เนื่องจากไม่มีการแสดงบรรพบุรุษ แต่จะแสดงขึ้น
Marco Eckstein

โหวตแล้ว! ฉันใช้สิ่งนี้ในคำตอบ stackoverflow อื่น: stackoverflow.com/questions/48792245/…
Nick Timmer

น่าเสียดายที่สิ่งนี้ใช้ได้เฉพาะเมื่อใช้สไตล์อินไลน์กับองค์ประกอบเท่านั้นไม่ใช่เมื่อการเปลี่ยนแปลงเป็นผลมาจากการเปลี่ยนแปลงของคลาส CSS (ซึ่งเป็นสถานการณ์ที่มีแนวโน้มมากขึ้นเนื่องจากในสถานการณ์ก่อนหน้านี้คุณอาจมีการควบคุมแบบเป็นโปรแกรมทั้งหมดอยู่แล้ว) ซอแสดงให้เห็นว่าปัญหา: jsfiddle.net/elmarj/uye62Lxc/4 ดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีสังเกตการเปลี่ยนแปลงสไตล์ได้ที่นี่: dev.to/oleggromov/observes-style-changes---d4f
Elmar Jansen

จริงๆแล้ว Observer ที่ถูกต้องที่จะใช้ที่นี่คือIntersectionObserver- stackoverflow.com/a/52627221/2803743
kano

โซลูชันนี้มีประโยชน์เมื่อคุณต้องการเปิดและปิดองค์ประกอบและไม่มีการควบคุมองค์ประกอบนั้นโดยตรงและต้องการตอบสนองต่อการเปลี่ยนแปลงค่าที่แสดง
Eran Goldin

15

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

var previous_style,
    poll = window.setInterval(function()
{
    var current_style = document.getElementById('target').style.display;
    if (previous_style != current_style) {
        alert('style changed');
        window.clearInterval(poll);
    } else {
        previous_style = current_style;
    }
}, 100);

มาตรฐาน DOM ยังระบุเหตุการณ์การกลายพันธุ์ด้วย แต่ฉันไม่เคยมีโอกาสได้ใช้และฉันไม่แน่ใจว่ามันรองรับได้ดีแค่ไหน คุณจะใช้มันในลักษณะนี้:

target.addEventListener('DOMAttrModified', function()
{
    if (e.attrName == 'style') {
        alert('style changed');
    }
}, false);

รหัสนี้อยู่ด้านบนของหัวของฉันฉันจึงไม่แน่ใจว่าจะใช้ได้หรือไม่

ที่ดีที่สุดและง่ายที่สุดในการแก้ปัญหาที่จะมีการเรียกกลับในฟังก์ชั่นการแสดงเป้าหมายของคุณ


1
น่าสนใจขอบคุณ ไม่กี่ปีต่อมาสถานะของ DOMAttrModified ได้เปลี่ยนไป: "เลิกใช้งานคุณลักษณะนี้ได้ถูกลบออกจากมาตรฐานเว็บแล้วแม้ว่าเบราว์เซอร์บางตัวอาจยังสนับสนุนอยู่ แต่ก็อยู่ในขั้นตอนการยกเลิก" (Mozilla)
BurninLeo


14

ฉันมีปัญหาเดียวกันนี้และสร้างปลั๊กอิน jQuery เพื่อแก้ปัญหาสำหรับไซต์ของเรา

https://github.com/shaunbowe/jquery.visibilityChanged

นี่คือวิธีที่คุณจะใช้ตามตัวอย่างของคุณ:

$('#contentDiv').visibilityChanged(function(element, visible) {
    alert("do something");
});

4
โซลูชันนี้ใช้.is(':visible')ร่วมกับsetTimeout.
Hp93

7

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

อย่างไรก็ตามเพื่อวัตถุประสงค์ในการตอบคำถาม (และใครก็ตามที่สร้าง Chrome หรือ Firefox Extensionsซึ่งเป็นกรณีการใช้งานทั่วไป) Mutation SummaryและMutation Observerจะอนุญาตให้การเปลี่ยนแปลง DOM ทริกเกอร์เหตุการณ์

ตัวอย่างเช่นการทริกเกอร์เหตุการณ์สำหรับองค์ประกอบที่มีdata-widgetการเพิ่มแอตทริบิวต์ไปยัง DOM ยืมตัวอย่างที่ยอดเยี่ยมนี้จากบล็อกของ David Walsh :

var observer = new MutationObserver(function(mutations) {
    // For the sake of...observation...let's output the mutation to console to see how this all works
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
    });    
});

// Notify me of everything!
var observerConfig = {
    attributes: true, 
    childList: true, 
    characterData: true 
};

// Node, config
// In this case we'll listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);

คำตอบรวมadded, removed,valueChangedและอื่น ๆ valueChangedรวมคุณสมบัติทั้งหมดรวมถึงdisplayอื่น ๆ


อาจเป็นเพราะเขาอยากรู้ว่าเมื่อไหร่จะฉายจริง ฉันทำได้ฉันสามารถถือว่าผู้โหวตลดลงมีเหตุผลเดียวกันเท่านั้น
Dave Hillier

ไม่ตอบคำถามและไม่แนะนำให้โพสต์เฉพาะลิงก์
Alex Holsgrove

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

โปรดทราบว่าสิ่งนี้ยังไม่ครอบคลุมถึงส่วนที่อธิบายว่าในกระบวนการสังเกตการกลายพันธุ์ "การมองเห็น" เข้ามาที่ใด
Mike 'Pomax' Kamermans


3

เพียงเพื่อแสดงความคิดเห็นเกี่ยวกับการสนับสนุนเบราว์เซอร์ตัวฟังเหตุการณ์ DOMAttrModified:

รองรับข้ามเบราว์เซอร์

เหตุการณ์เหล่านี้จะไม่ถูกนำไปใช้อย่างสม่ำเสมอในเบราว์เซอร์ต่างๆตัวอย่างเช่น:

  • IE ก่อนเวอร์ชัน 9 ไม่รองรับเหตุการณ์การกลายพันธุ์เลยและไม่ได้ใช้งานบางอย่างอย่างถูกต้องในเวอร์ชัน 9 (ตัวอย่างเช่น DOMNodeInserted)

  • WebKit ไม่รองรับ DOMAttrModified (ดูข้อผิดพลาดของ webkit 8191 และวิธีแก้ไขปัญหาชั่วคราว)

  • "เหตุการณ์ชื่อการกลายพันธุ์" เช่น DOMElementNameChanged และ DOMAttributeNameChanged ไม่ได้รับการสนับสนุนใน Firefox (ในเวอร์ชัน 11) และอาจอยู่ในเบราว์เซอร์อื่นด้วย

ที่มา: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events


-2

ทางออกของฉัน:

; (function ($) {
$.each([ "toggle", "show", "hide" ], function( i, name ) {
    var cssFn = $.fn[ name ];
    $.fn[ name ] = function( speed, easing, callback ) {
        if(speed == null || typeof speed === "boolean"){
            var ret=cssFn.apply( this, arguments )
            $.fn.triggerVisibleEvent.apply(this,arguments)
            return ret
        }else{
            var that=this
            var new_callback=function(){
                callback.call(this)
                $.fn.triggerVisibleEvent.apply(that,arguments)
            }
            var ret=this.animate( genFx( name, true ), speed, easing, new_callback )
            return ret
        }
    };
});

$.fn.triggerVisibleEvent=function(){
    this.each(function(){
        if($(this).is(':visible')){
            $(this).trigger('visible')
            $(this).find('[data-trigger-visible-event]').triggerVisibleEvent()
        }
    })
}
})(jQuery);

ตัวอย่างเช่น:

if(!$info_center.is(':visible')){
    $info_center.attr('data-trigger-visible-event','true').one('visible',processMoreLessButton)
}else{
    processMoreLessButton()
}

function processMoreLessButton(){
//some logic
}

5
คุณควรบอกทุกคนว่าคุณเคยใช้โค้ดดั้งเดิมของ jQuery มันจะสร้างแรงบันดาลใจให้มีความมั่นใจมากขึ้น)
aemonge

1
"ทางออกของฉัน". ประสาทกับผู้ชายคนนี้
André Silva

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