jQuery-Ereignis zum Auslösen einer Aktion, wenn ein div sichtbar gemacht wird


312

Ich verwende jQuery auf meiner Site und möchte bestimmte Aktionen auslösen, wenn ein bestimmtes Div sichtbar gemacht wird.

Ist es möglich, eine Art "sichtbarer" Ereignishandler an beliebige Divs anzuhängen und bestimmten Code auszuführen, wenn der Div sichtbar gemacht wird?

Ich möchte so etwas wie den folgenden Pseudocode:

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

Der Alarmcode ("etwas tun") sollte erst ausgelöst werden, wenn contentDiv tatsächlich sichtbar gemacht wird.

Vielen Dank.


Antworten:


190

Sie können die ursprüngliche .show () -Methode jederzeit ergänzen, damit Sie nicht jedes Mal Ereignisse auslösen müssen, wenn Sie etwas anzeigen oder wenn Sie es für die Arbeit mit Legacy-Code benötigen:

Jquery-Erweiterung:

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

Anwendungsbeispiel:

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

Auf diese Weise können Sie effektiv etwas vor und nach der Show ausführen, während Sie das normale Verhalten der ursprünglichen .show () -Methode ausführen.

Sie können auch eine andere Methode erstellen, damit Sie die ursprüngliche .show () -Methode nicht überschreiben müssen.


7
BEARBEITEN: Diese Methode hat nur einen Nachteil: Sie müssen dieselbe "Erweiterung" für alle Methoden wiederholen, die das Element enthüllen: show (), slideDown () usw. Um dieses Problem einmal und einmal zu lösen, ist etwas Universelleres erforderlich alles, da es unmöglich ist, ein "bereites" Ereignis für delegate () oder live () zu haben.
Shahriyar

1
Gut, das einzige Problem ist, dass die fadeToFunktion nach der Implementierung dieser Funktion nicht richtig funktioniert
Omid

9
Ihr Code scheint nicht mit der neuesten jQuery zu funktionieren (1.7.1 zum Datum dieses Kommentars). Ich habe diese Lösung leicht überarbeitet, um mit der neuesten jQuery zu arbeiten: stackoverflow.com/a/9422207/135968
mkmurray

1
Dieser Code funktioniert nicht mit der durch eine Ajax-Antwort ausgelösten Div-Sichtbarkeit.
JackTheKnife

Neuling hier. Ich verstehe nicht, warum auf obj (und newCallback) außerhalb der function () - Deklaration verwiesen werden kann, wie in apply () zu sehen ist. Mir wurde beigebracht, dass das Deklarieren mit var Variablen lokal macht, aber das Entfernen von "var" macht sie automatisch global.
Yuta73

85

Das Problem wird von Beobachtern der DOM-Mutation angesprochen . Mit ihnen können Sie einen Beobachter (eine Funktion) an Ereignisse binden, bei denen sich Inhalt, Text oder Attribute von dom-Elementen ändern.

Mit der Veröffentlichung von IE11 unterstützen alle gängigen Browser diese Funktion. Überprüfen Sie http://caniuse.com/mutationobserver

Der Beispielcode lautet wie folgt:

$(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
Schade, dass IE es noch nicht unterstützt. caniuse.com/mutationobserver -> Anzeigen der Browser, die dies unterstützen.
ccsakuweb

2
Dies funktioniert in der Tat und ich muss keine älteren Browser unterstützen, also ist es perfekt! Ich habe hier einen JSFiddle-Beweis für die Antwort hinzugefügt: jsfiddle.net/DanAtkinson/26URF
Dan Atkinson

funktioniert gut in Chrom, funktioniert aber nicht in Blackberry 10 Cascade Webview (wenn es jemand anderes interessiert;))
Guillaume Gendre

1
Dies scheint nicht zu funktionieren, wenn die Änderung der Sichtbarkeit durch eine Attributänderung in einem Vorfahren des überwachten Vorfahren verursacht wird.
Michael

4
Dies scheint in Chrome 51 nicht zu funktionieren, nicht sicher warum. Führen Sie den obigen Code aus und drücken Sie die Taste, keine Warnung.
Kris

76

Es gibt kein natives Ereignis, in das Sie sich einbinden können. Sie können jedoch ein Ereignis aus Ihrem Skript auslösen, nachdem Sie das div mithilfe von sichtbar gemacht haben. Trigger - Funktion

z.B

//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
Meine Einschränkung hier ist, dass ich nicht unbedingt Zugriff auf den Code habe, der show () 's mein div ist. Daher könnte ich die Methode trigger () nicht aufrufen.
Frankadelic

11
Der JS wurde von einem Entwicklungsteam außerhalb meiner Organisation bereitgestellt. Es ist auch so etwas wie eine "Black Box", daher möchten wir diesen Code nach Möglichkeit nicht ändern. Es kann jedoch unsere einzige Wahl sein.
Frankadelic

Sie können ihre js-Funktionen jederzeit mit Ihrer eigenen Implementierung überschreiben. Klingt jedoch düster!
Redsquare

11
@redsquare: Was ist, wenn show()von mehreren anderen Stellen als dem oben beschriebenen Codeblock aufgerufen wird?
Robin Maben

1
In diesem Beispiel sollten Sie den Funktionsnamen in ändern, onIsVisibleda die Verwendung von "isVisible" derzeit etwas mehrdeutig ist.
Brad Johnson

27

Sie können das Live Query-Plugin von jQuery verwenden . Und schreiben Sie den Code wie folgt:

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

Jedes Mal, wenn contentDiv sichtbar ist, wird "etwas tun" benachrichtigt!


Na verdammt, das funktioniert. Ich hatte darüber nachgedacht und es als unwahrscheinlich abgelehnt, ohne es zu versuchen. Hätte es versuchen sollen. :)
Neminem

1
Hat bei mir nicht funktioniert. Ich erhalte die Fehlermeldung "Livequery ist keine Funktion". Versucht mit "jquery-1.12.4.min.js" und "jquery-3.1.1.min.js"
Paul Gorbas

2
@ Paul: Es ist ein Plugin
Christian

Dies kann Ihre Website erheblich verlangsamen! Das Livequery-Plugin führt eine schnelle Abfrage der Attribute durch, anstatt moderne effiziente Methoden wie DOM-Mutationsbeobachter zu verwenden. Daher würde ich die Lösung von @hegemon bevorzugen: stackoverflow.com/a/16462443/19163 (und Polling nur als Fallback für alte IE-Versionen verwenden) - vog vor 1 Stunde
vog

20

Die Lösung von redsquare ist die richtige Antwort.

Als IN-THEORY-Lösung können Sie jedoch eine Funktion schreiben, die die nach .visibilityCheck( nicht alle sichtbaren Elemente ) klassifizierten Elemente auswählt und deren visibilityEigenschaftswert überprüft . wenn truedann etwas tun.

Danach sollte die Funktion regelmäßig mit der setInterval()Funktion ausgeführt werden. Sie können den Timer mit der Taste stoppenclearInterval() nach erfolgreichem Abruf .

Hier ist ein Beispiel:

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

window.setInterval(foo, 100);

Sie können auch einige Leistungsverbesserungen vornehmen. Die Lösung ist jedoch im Grunde genommen absurd, um in Aktion verwendet zu werden. So...


5
Keine gute Form, um eine implizite Funktion in setTimeout / setInterval zu verwenden. Verwenden Sie setTimeout (foo, 100)
redsquare

1
Ich muss es Ihnen geben, das ist kreativ, wenn auch böse. Und es war gerade schnell und schmutzig genug, um den Trick für mich auf IE8-konforme Weise zu erledigen. Danke dir!
JD Smith

oder display:none?
Michael

12

Der folgende Code (abgerufen von http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/ ) ermöglicht Ihnen die Verwendung $('#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
Dies hat bei mir perfekt funktioniert, aber es ist wichtig zu beachten, dass die Funktion die Verkettung in den Show- und Hide-Funktionen unterbricht, wodurch viele Plugins unterbrochen werden. Fügen Sie eine Rückgabe vor hinzu el.apply(this, arguments), um dies zu beheben.
Jaimerump

Das habe ich gesucht! Die Rückgabe muss wie im Kommentar von @jaimerump
Brainfeeder

9

Wenn Sie das Ereignis für alle Elemente (und untergeordneten Elemente) auslösen möchten, die tatsächlich sichtbar gemacht werden, durch $ .show, toggle, toggleClass, addClass oder 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;
    }
});

Und jetzt dein Element:

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

Sie können zusätzlichen jQuery-Funktionen Überschreibungen hinzufügen, indem Sie sie dem Array oben hinzufügen (z. B. "attr").


9

Ein Auslöser für das Ausblenden / Zeigen von Ereignissen, der auf Glenns Idee basiert: Das Umschalten wurde entfernt, da es das Ausblenden / Ausblenden auslöst und wir keine 2 Feuer für ein Ereignis wollen

$(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
sollten Sie auch attr und removeAttr überprüfen?
Andrija

5

Ich hatte das gleiche Problem und habe ein jQuery-Plugin erstellt, um es für unsere Site zu lösen.

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

So würden Sie es anhand Ihres Beispiels verwenden:

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

1
Polling ist nicht sehr effizient und würde den Browser erheblich verlangsamen, wenn dies für viele Elemente verwendet würde.
Cerin


4

Was mir hier geholfen hat, ist die aktuelle Polyfill mit ResizeObserver-Spezifikation :

const divEl = $('#section60');

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

Beachten Sie, dass es Crossbrowser und Performant ist (kein Polling).


Funktionierte gut, um jedes Mal zu erkennen, wenn eine Tabellenzeile im Gegensatz zu anderen angezeigt / ausgeblendet wurde. Ein Plus ist auch, dass kein Plugin erforderlich war!
BrettC

3

Ich habe eine einfache Setintervall-Funktion ausgeführt, um dies zu erreichen. Wenn ein Element mit der Klasse div1 sichtbar ist, wird div2 als sichtbar festgelegt. Ich kenne keine gute Methode, aber eine einfache Lösung.

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


2

Diese Unterstützung erleichtert und löst ein Ereignis aus, nachdem die Animation abgeschlossen ist! [getestet auf 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);

Inspiriert von http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/


2

Binden Sie einfach einen Trigger mit dem Selektor und fügen Sie den Code in das Triggerereignis ein:

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

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

Ich denke, das ist eine ziemlich elegante Lösung. Es hat bei mir funktioniert.
KIKO Software

1

Es gibt ein jQuery-Plugin, mit dem Sie Änderungen an DOM-Attributen beobachten können.

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

Das Plugin wird verpackt. Sie müssen lediglich MutationObserver binden

Sie können es dann verwenden, um das div zu beobachten, indem Sie:

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

1

Hoffe, dies wird die Arbeit auf einfachste Weise erledigen:

$("#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

Ich habe den Auslöser für das Ausblenden / Anzeigen von Ereignissen in Catalint basierend auf Glenns Idee geändert. Mein Problem war, dass ich eine modulare Anwendung habe. Ich wechsle zwischen Modulen, die Divs-Eltern ein- und ausblenden. Wenn ich dann ein Modul verstecke und ein anderes zeige, habe ich mit seiner Methode eine sichtbare Verzögerung, wenn ich zwischen Modulen wechsle. Ich brauche dieses Ereignis nur manchmal zu beleuchten, und bei einigen besonderen Kindern. Deshalb habe ich beschlossen, nur die Kinder mit der Klasse "displayObserver" zu benachrichtigen.

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

Wenn ein Kind dann auf das Ereignis "Zeigen" oder "Verstecken" hören möchte, muss ich ihm die Klasse "displayObserver" hinzufügen, und wenn es nicht weiter hören möchte, entferne ich ihm die Klasse

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

Ich wünsche Hilfe


0

Ein Weg, dies zu tun.
Funktioniert nur bei Sichtbarkeitsänderungen, die durch CSS-Klassenänderungen vorgenommen werden, kann jedoch erweitert werden, um auch auf Attributänderungen zu achten.

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

meine Lösung:

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

Anwendungsbeispiel:

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

Diese automatische Code-Steuerung erfordert keine sichtbare oder nicht sichtbare Abfrage

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.