เหตุการณ์ jQuery เพื่อเรียกใช้การกระทำเมื่อมองเห็น div


312

ฉันใช้ jQuery ในเว็บไซต์ของฉันและฉันต้องการที่จะเรียกการกระทำบางอย่างเมื่อมีการมองเห็น div บางอย่าง

เป็นไปได้หรือไม่ที่จะแนบตัวจัดการเหตุการณ์ "isvisible" กับ divs ที่กำหนดเองและรันโค้ดบางอย่างเมื่อ div นั้นมองเห็นได้?

ฉันต้องการบางอย่างเช่นรหัสเทียมต่อไปนี้:

$(function() {
  $('#contentDiv').isvisible(function() {
    alert("do something");
  });
});

โค้ดการเตือน ("ทำบางสิ่ง") ไม่ควรเริ่มทำงานจนกว่า contentDiv จะทำให้มองเห็นได้จริง

ขอบคุณ


คำตอบ:


190

คุณสามารถเพิ่มวิธี. show ()ดั้งเดิมได้ตลอดเวลาเพื่อให้คุณไม่ต้องเรียกใช้เหตุการณ์ทุกครั้งที่คุณแสดงบางอย่างหรือหากคุณต้องการให้ทำงานกับรหัสดั้งเดิม:

นามสกุล Jquery:

jQuery(function($) {

  var _oldShow = $.fn.show;

  $.fn.show = function(speed, oldCallback) {
    return $(this).each(function() {
      var obj         = $(this),
          newCallback = function() {
            if ($.isFunction(oldCallback)) {
              oldCallback.apply(obj);
            }
            obj.trigger('afterShow');
          };

      // you can trigger a before show if you want
      obj.trigger('beforeShow');

      // now use the old function to show the element passing the new callback
      _oldShow.apply(obj, [speed, newCallback]);
    });
  }
});

ตัวอย่างการใช้งาน:

jQuery(function($) {
  $('#test')
    .bind('beforeShow', function() {
      alert('beforeShow');
    }) 
    .bind('afterShow', function() {
      alert('afterShow');
    })
    .show(1000, function() {
      alert('in show callback');
    })
    .show();
});

สิ่งนี้จะช่วยให้คุณทำอะไรบางอย่างได้อย่างมีประสิทธิภาพก่อนแสดงและ afterShow ในขณะที่ยังคงดำเนินการตามปกติของวิธีการ. show () เดิม

คุณสามารถสร้างวิธีอื่นได้ดังนั้นคุณไม่จำเป็นต้องแทนที่เมธอด. show () ดั้งเดิม


7
แก้ไข: มีข้อเสียเพียงอย่างเดียวด้วยวิธีนี้: คุณจะต้องทำซ้ำ "นามสกุล" เดียวกันสำหรับวิธีการทั้งหมดที่เปิดเผยองค์ประกอบ: show (), slideDown () ฯลฯ สิ่งที่เป็นสากลมากขึ้นจะต้องแก้ปัญหานี้ครั้งเดียวและ ทั้งหมดเนื่องจากเป็นไปไม่ได้ที่จะมีเหตุการณ์ "พร้อม" สำหรับตัวแทน () หรือสด ()
Shahriyar Imanov

1
ดีปัญหาเดียวคือfadeToฟังก์ชั่นทำงานไม่ถูกต้องหลังจากใช้งานฟังก์ชั่นนี้แล้ว
Omid

9
รหัสของคุณไม่ทำงานกับ jQuery ล่าสุด (1.7.1 ณ วันที่ความคิดเห็นนี้) ฉันได้ทำใหม่โซลูชันนี้เล็กน้อยเพื่อทำงานกับ jQuery ล่าสุด: stackoverflow.com/a/9422207/135968
mkmurray

1
ไม่สามารถรับรหัสนั้นเพื่อทำงานกับการมองเห็น div ที่ถูกทริกเกอร์โดย ajax response
JackTheKnife

มือใหม่ที่นี่ ฉันไม่เข้าใจว่าทำไม obj (และ newCallback) สามารถอ้างอิงได้นอกฟังก์ชั่น () การประกาศตามที่เห็นในนำไปใช้ () ฉันถูกสอนว่าการประกาศด้วย var ทำให้ตัวแปรท้องถิ่น แต่การลบ "var" ทำให้พวกมันเป็นแบบโกลบอลโดยอัตโนมัติ
Yuta73

85

ปัญหาจะถูกแก้ไขโดยการสังเกตการณ์การกลายพันธุ์ DOM พวกมันอนุญาตให้คุณผูกผู้สังเกตการณ์ (ฟังก์ชัน) กับเหตุการณ์ของการเปลี่ยนแปลงเนื้อหาข้อความหรือคุณลักษณะขององค์ประกอบ dom

ด้วยการเปิดตัว IE11 เบราว์เซอร์ที่สำคัญทั้งหมดรองรับคุณสมบัตินี้ให้ตรวจสอบhttp://caniuse.com/mutationobserver

โค้ดตัวอย่างมีดังต่อไปนี้:

$(function() {
  $('#show').click(function() {
    $('#testdiv').show();
  });

  var observer = new MutationObserver(function(mutations) {
    alert('Attributes changed!');
  });
  var target = document.querySelector('#testdiv');
  observer.observe(target, {
    attributes: true
  });

});
<div id="testdiv" style="display:none;">hidden</div>
<button id="show">Show hidden div</button>

<script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>


3
เป็นเรื่องที่น่าเสียดายที่ IE ยังไม่รองรับ caniuse.com/mutationobserver -> เพื่อดูเบราว์เซอร์ที่รองรับ
ccsakuweb

2
มันใช้งานได้จริงและฉันไม่ต้องการสนับสนุนเบราว์เซอร์รุ่นเก่าจึงสมบูรณ์แบบ! ฉันได้เพิ่มหลักฐาน JSFiddle ของคำตอบที่นี่: jsfiddle.net/DanAtkinson/26URF
Dan Atkinson

ทำงานได้ดีใน Chrome แต่ไม่สามารถใช้งานได้ใน BlackBerry 10 casview webview (หากมีใครสนใจ))
Guillaume Gendre

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

4
ดูเหมือนจะใช้งานไม่ได้ใน Chrome 51 ไม่แน่ใจว่าทำไม เรียกใช้รหัสข้างต้นและกดปุ่มไม่มีการแจ้งเตือน
กริช

76

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

เช่น

//declare event to run when div is visible
function isVisible(){
   //do something

}

//hookup the event
$('#someDivId').bind('isVisible', isVisible);

//show div and trigger custom event in callback when div is visible
$('#someDivId').show('slow', function(){
    $(this).trigger('isVisible');
});

45
ข้อ จำกัด ของฉันที่นี่คือฉันไม่จำเป็นต้องเข้าถึงรหัสที่แสดง () ของ div ของฉัน ดังนั้นฉันจะไม่สามารถเรียกวิธีการทริกเกอร์ () ได้
frankadelic

11
JS จัดทำโดยทีมพัฒนานอกองค์กรของฉัน มันเป็นของ "กล่องดำ" ดังนั้นเราจึงไม่ต้องการแก้ไขโค้ดนั้นถ้าเป็นไปได้ มันอาจเป็นทางเลือกเดียวของเรา
frankadelic

คุณสามารถประทับตราฟังก์ชั่น js ของพวกเขาได้ตลอดเวลา เสียงน่ากลัว แต่!
redsquare

11
@redsquare: จะเกิดอะไรขึ้นหากshow()มีการเรียกจากหลาย ๆ ที่นอกเหนือจากรหัสบล็อกที่กล่าวถึงข้างต้น
Robin Maben

1
สำหรับตัวอย่างนี้คุณควรเปลี่ยนชื่อฟังก์ชั่นเป็นonIsVisibleเพราะการใช้ "isVisible" เล็กน้อยคลุมเครือในขณะนี้
แบรดจอห์นสัน

27

คุณสามารถใช้ปลั๊กอิน Live Queryของ jQuery ได้ และเขียนโค้ดดังนี้

$('#contentDiv:visible').livequery(function() {
    alert("do something");
});

จากนั้นทุกครั้งที่ contentDiv ปรากฏให้เห็น "ทำอะไรบางอย่าง" จะได้รับการแจ้งเตือน!


ดีที่ทำงาน ฉันคิดเกี่ยวกับมันและปฏิเสธว่าไม่น่าจะทำงานได้โดยไม่ต้องลอง ควรลองทำดู :)
neminem

1
ไม่ได้ผลสำหรับฉัน ฉันได้รับข้อผิดพลาด "livequery ไม่ใช่ฟังก์ชั่น" พยายามทั้ง "jquery-1.12.4.min.js" และ "jquery-3.1.1.min.js"
Paul Gorbas

2
@Paul: มันเป็นปลั๊กอิน
Christian

นี่อาจทำให้เว็บไซต์ของคุณช้าลงอย่างมาก! ปลั๊กอิน livequery ทำการสำรวจอย่างรวดเร็วกับแอตทริบิวต์แทนที่จะใช้วิธีการที่มีประสิทธิภาพทันสมัยเช่นผู้สังเกตการณ์การกลายพันธุ์ของ DOM ดังนั้นฉันจึงชอบวิธีแก้ปัญหาของ @hegemon: stackoverflow.com/a/16462443/19163 (และใช้การโพลเป็นทางเลือกสำหรับ IE เวอร์ชันเก่าเท่านั้น) - vog 1 ชั่วโมงที่ผ่านมา
vog

20

โซลูชันของ redsquareเป็นคำตอบที่ถูกต้อง

แต่เป็นวิธีการแก้ปัญหาในทฤษฎีคุณสามารถเขียนฟังก์ชั่นที่เลือกองค์ประกอบที่จัดประเภทโดย.visibilityCheck( ไม่ใช่องค์ประกอบที่มองเห็นได้ทั้งหมด ) และตรวจสอบvisibilityค่าคุณสมบัติของพวกเขา; ถ้าtrueทำอะไรแล้ว

หลังจากนั้นฟังก์ชั่นควรจะดำเนินการเป็นระยะโดยใช้setInterval()ฟังก์ชั่น คุณสามารถหยุดจับเวลาโดยใช้การclearInterval()โทรออกได้สำเร็จ

นี่คือตัวอย่าง:

function foo() {
    $('.visibilityCheck').each(function() {
        if ($(this).is(':visible')){
            // do something
        }
    });
}

window.setInterval(foo, 100);

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


5
ฟอร์มไม่ดีในการใช้ func โดยนัยภายใน setTimeout / setInterval ใช้ setTimeout (foo, 100)
redsquare

1
ฉันต้องมอบมันให้กับคุณนี่เป็นความคิดสร้างสรรค์ แต่น่ารังเกียจ และมันก็เร็วและสกปรกมากพอที่จะทำกลอุบายให้ฉันด้วยวิธีที่สอดคล้องกับ IE8 ขอบคุณ!
JD Smith

หรือdisplay:none?
Michael

12

รหัสต่อไปนี้ (ดึงมาจากhttp://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/ ) จะช่วยให้คุณใช้งาน$('#someDiv').on('show', someFunc);ได้

(function ($) {
  $.each(['show', 'hide'], function (i, ev) {
    var el = $.fn[ev];
    $.fn[ev] = function () {
      this.trigger(ev);
      return el.apply(this, arguments);
    };
  });
})(jQuery);

8
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน แต่สิ่งสำคัญคือต้องทราบว่าฟังก์ชั่นหยุดการแสดงและซ่อนฟังก์ชั่นซึ่งแบ่งปลั๊กอินจำนวนมาก เพิ่มการย้อนกลับel.apply(this, arguments)เพื่อแก้ไขปัญหานี้
jaimerump

นี่คือสิ่งที่ฉันกำลังมองหา! จะต้องเพิ่มการส่งคืนตามความคิดเห็นจาก @jaimerump
Brainfeeder

9

หากคุณต้องการทริกเกอร์เหตุการณ์บนองค์ประกอบทั้งหมด (และองค์ประกอบย่อย) ที่ทำให้มองเห็นได้จริงโดย $ .show, toggle, toggleClass, addClass หรือ removeClass:

$.each(["show", "toggle", "toggleClass", "addClass", "removeClass"], function(){
    var _oldFn = $.fn[this];
    $.fn[this] = function(){
        var hidden = this.find(":hidden").add(this.filter(":hidden"));
        var result = _oldFn.apply(this, arguments);
        hidden.filter(":visible").each(function(){
            $(this).triggerHandler("show"); //No bubbling
        });
        return result;
    }
});

และตอนนี้องค์ประกอบของคุณ:

$("#myLazyUl").bind("show", function(){
    alert(this);
});

คุณสามารถเพิ่มการแทนที่ลงในฟังก์ชั่น jQuery เพิ่มเติมโดยการเพิ่มลงในอาร์เรย์ที่ด้านบน (เช่น "attr")


9

ทริกเกอร์เหตุการณ์ซ่อน / แสดงตาม Glenns ideea: toggle ถูกลบออกเนื่องจากไฟไหม้แสดง / ซ่อนและเราไม่ต้องการ 2 ไฟสำหรับเหตุการณ์หนึ่ง

$(function(){
    $.each(["show","hide", "toggleClass", "addClass", "removeClass"], function(){
        var _oldFn = $.fn[this];
        $.fn[this] = function(){
            var hidden = this.find(":hidden").add(this.filter(":hidden"));
            var visible = this.find(":visible").add(this.filter(":visible"));
            var result = _oldFn.apply(this, arguments);
            hidden.filter(":visible").each(function(){
                $(this).triggerHandler("show");
            });
            visible.filter(":hidden").each(function(){
                $(this).triggerHandler("hide");
            });
            return result;
        }
    });
});

2
คุณควรตรวจสอบ attr และ removeAttr ด้วยหรือไม่
Andrija

5

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

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

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

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

1
การทำโพลไม่ได้มีประสิทธิภาพมากและจะทำให้เบราว์เซอร์ช้าลงหากใช้กับองค์ประกอบหลายอย่าง
Cerin

4

ใช้ jQuery Waypoints:

$('#contentDiv').waypoint(function() {
   alert('do something');
});

ตัวอย่างอื่น ๆ บนเว็บไซต์ของ jQuery Waypoints


1
ใช้ได้เฉพาะเมื่อรายการมองเห็นได้เนื่องจากการเลื่อนและไม่ได้เกิดจากการเปลี่ยนแปลงอื่น ๆ
AdamJones

@ Adamones เมื่อฉันใช้คีย์บอร์ดมันทำงานได้อย่างที่คาดไว้ ตัวอย่าง: imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll
Fedir RYKHTIK

4

สิ่งที่ช่วยฉันได้ที่นี่คือResizeObserver spec polyfillล่าสุด:

const divEl = $('#section60');

const ro = new ResizeObserver(() => {
    if (divEl.is(':visible')) {
        console.log("it's visible now!");
    }
});
ro.observe(divEl[0]);

โปรดทราบว่ามันเป็น crossbrowser และนักแสดง (ไม่มีการสำรวจ)


ทำงานได้ดีสำหรับการตรวจจับในแต่ละครั้งที่แถวของตารางแสดง / ซ่อนเหมือนคนอื่น ๆ และข้อดีก็คือไม่มีปลั๊กอินที่ต้องใช้!
BrettC

3

ฉันใช้ฟังก์ชัน setinterval อย่างง่ายเพื่อให้ได้สิ่งนี้ หากองค์ประกอบที่มี class div1 มองเห็นได้ก็จะทำให้ div2 นั้นมองเห็นได้ ฉันรู้ว่าไม่ใช่วิธีที่ดี แต่เป็นการแก้ไขที่ง่าย

setInterval(function(){
  if($('.div1').is(':visible')){
    $('.div2').show();
  }
  else {
    $('.div2').hide();
  }      
}, 100);


2

การสนับสนุนนี้ช่วยให้ผ่อนคลายและกระตุ้นให้เกิดเหตุการณ์หลังจากทำแอนิเมชันแล้ว! [ทดสอบกับ jQuery 2.2.4]

(function ($) {
    $.each(['show', 'hide', 'fadeOut', 'fadeIn'], function (i, ev) {
        var el = $.fn[ev];
        $.fn[ev] = function () {
            var result = el.apply(this, arguments);
            var _self=this;
            result.promise().done(function () {
                _self.triggerHandler(ev, [result]);
                //console.log(_self);
            });
            return result;
        };
    });
})(jQuery);

แรงบันดาลใจจากhttp://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/


2

เพียงแค่ผูกทริกเกอร์ด้วยตัวเลือกและใส่รหัสในเหตุการณ์ทริกเกอร์:

jQuery(function() {
  jQuery("#contentDiv:hidden").show().trigger('show');

  jQuery('#contentDiv').on('show', function() {
    console.log('#contentDiv is now visible');
    // your code here
  });
});

ฉันคิดว่านี่เป็นทางออกที่งดงามมาก มันใช้งานได้สำหรับฉัน
ซอฟต์แวร์ KIKO

1

มีปลั๊กอิน jQuery สำหรับดูการเปลี่ยนแปลงในแอตทริบิวต์ DOM

https://github.com/darcyclarke/jQuery-Watch-Plugin

ปลั๊กอินล้อมรอบทั้งหมดที่คุณต้องทำคือผูก MutationObserver

จากนั้นคุณสามารถใช้มันเพื่อดู div โดยใช้:

$("#selector").watch('css', function() {
    console.log("Visibility: " + this.style.display == 'none'?'hidden':'shown'));
    //or any random events
});

1

หวังว่าสิ่งนี้จะทำงานในลักษณะที่ง่ายที่สุด:

$("#myID").on('show').trigger('displayShow');

$('#myID').off('displayShow').on('displayShow', function(e) {
    console.log('This event will be triggered when myID will be visible');
});

0

ฉันเปลี่ยนทริกเกอร์เหตุการณ์ซ่อน / แสดงจาก Catalint ตามแนวคิดของ Glenn ปัญหาของฉันคือฉันมีแอปพลิเคชันแบบแยกส่วน ฉันเปลี่ยนระหว่างโมดูลที่แสดงและซ่อนผู้ปกครอง divs จากนั้นเมื่อฉันซ่อนโมดูลและแสดงอีกโมดูลด้วยวิธีของเขาฉันมีความล่าช้าที่มองเห็นได้เมื่อฉันเปลี่ยนระหว่างโมดูล บางครั้งฉันต้องการทำให้เหตุการณ์นี้สว่างขึ้นและในเด็กพิเศษบางคน ดังนั้นฉันตัดสินใจที่จะแจ้งเตือนเฉพาะเด็กที่มีคลาส "displayObserver"

$.each(["show", "hide", "toggleClass", "addClass", "removeClass"], function () {
    var _oldFn = $.fn[this];
    $.fn[this] = function () {
        var hidden = this.find(".displayObserver:hidden").add(this.filter(":hidden"));
        var visible = this.find(".displayObserver:visible").add(this.filter(":visible"));
        var result = _oldFn.apply(this, arguments);
        hidden.filter(":visible").each(function () {
            $(this).triggerHandler("show");
        }); 
        visible.filter(":hidden").each(function () {
            $(this).triggerHandler("hide");
        });
        return result;
    }
});

จากนั้นเมื่อเด็กต้องการฟังเหตุการณ์ "แสดง" หรือ "ซ่อน" ฉันต้องเพิ่มคลาส "displayObserver" และเมื่อไม่ต้องการฟังต่อฉันจะลบชั้นเรียนออก

bindDisplayEvent: function () {
   $("#child1").addClass("displayObserver");
   $("#child1").off("show", this.onParentShow);
   $("#child1").on("show", this.onParentShow);
},

bindDisplayEvent: function () {
   $("#child1").removeClass("displayObserver");
   $("#child1").off("show", this.onParentShow);
},

ฉันต้องการความช่วยเหลือ


0

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

var observer = new MutationObserver(function(mutations) {
        var clone = $(mutations[0].target).clone();
        clone.removeClass();
                for(var i = 0; i < mutations.length; i++){
                    clone.addClass(mutations[i].oldValue);
        }
        $(document.body).append(clone);
        var cloneVisibility = $(clone).is(":visible");
        $(clone).remove();
        if (cloneVisibility != $(mutations[0].target).is(":visible")){
            var visibilityChangedEvent = document.createEvent('Event');
            visibilityChangedEvent.initEvent('visibilityChanged', true, true);
            mutations[0].target.dispatchEvent(visibilityChangedEvent);
        }
});

var targets = $('.ui-collapsible-content');
$.each(targets, function(i,target){
        target.addEventListener('visibilityChanged',VisbilityChanedEventHandler});
        target.addEventListener('DOMNodeRemovedFromDocument',VisbilityChanedEventHandler });
        observer.observe(target, { attributes: true, attributeFilter : ['class'], childList: false, attributeOldValue: true });
    });

function VisbilityChanedEventHandler(e){console.log('Kaboom babe'); console.log(e.target); }

0

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

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

0
$( window ).scroll(function(e,i) {
    win_top = $( window ).scrollTop();
    win_bottom = $( window ).height() + win_top;
    //console.log( win_top,win_bottom );
    $('.onvisible').each(function()
    {
        t = $(this).offset().top;
        b = t + $(this).height();
        if( t > win_top && b < win_bottom )
            alert("do something");
    });
});

-2
<div id="welcometo"zhan</div>
<input type="button" name="ooo" 
       onclick="JavaScript:
                    if(document.all.welcometo.style.display=='none') {
                        document.all.welcometo.style.display='';
                    } else {
                        document.all.welcometo.style.display='none';
                    }">

การควบคุมอัตโนมัติของรหัสนี้ไม่จำเป็นต้องใช้การค้นหาที่มองเห็นหรือไม่สามารถควบคุมได้

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