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.

frankadelic
quelle
Siehe hier: stackoverflow.com/questions/1462138/…
Anderson Green

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.

Tres
quelle
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>

Hegemon
quelle
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');
});
rotes Quadrat
quelle
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!

ax003d
quelle
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...

sepehr
quelle
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);
JellicleCat
quelle
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").

Glenn Lane
quelle
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;
        }
    });
});
Catalint
quelle
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");
});
Shaun Bowe
quelle
1
Polling ist nicht sehr effizient und würde den Browser erheblich verlangsamen, wenn dies für viele Elemente verwendet würde.
Cerin
4

Verwenden Sie jQuery-Wegpunkte:

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

Weitere Beispiele auf der Website von jQuery Waypoints .

Fedir RYKHTIK
quelle
1
Dies funktioniert nur, wenn das Element durch Scrollen sichtbar wird und nicht durch andere programmatische Änderungen.
AdamJones
@AdamJones Wenn ich eine Tastatur verwende, funktioniert sie wie erwartet. Bsp.: Imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll
Fedir RYKHTIK
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).

Artem Vasiliev
quelle
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);
Ein Dip
quelle
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/

MSS
quelle
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
  });
});
Nikhil Gyan
quelle
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
});
tlogbon
quelle
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');
});
Prashant Anand Verma
quelle
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

ccsakuweb
quelle
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); }
Matas Vaitkevicius
quelle
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
}
user2699000
quelle
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");
    });
});
Saifullah Khan
quelle
-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

alpc
quelle