Evento jQuery per attivare l'azione quando viene reso visibile un div


312

Sto usando jQuery nel mio sito e vorrei innescare determinate azioni quando viene reso visibile un determinato div.

È possibile collegare una sorta di gestore di eventi "isvisible" a div arbitrari e avere un certo codice eseguito quando i div vengono resi visibili?

Vorrei qualcosa di simile al seguente pseudocodice:

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

Il codice di avviso ("fai qualcosa") non deve attivarsi fino a quando il contentDiv non viene reso visibile.

Grazie.


Risposte:


190

Puoi sempre aggiungere il metodo originale .show () in modo da non dover attivare eventi ogni volta che mostri qualcosa o se ne hai bisogno per funzionare con il codice legacy:

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

Esempio di utilizzo:

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

Questo ti consente effettivamente di fare qualcosa prima di Show e afterShow mentre esegui ancora il normale comportamento del metodo originale .show ().

È inoltre possibile creare un altro metodo in modo da non dover sovrascrivere il metodo originale .show ().


7
EDIT: c'è solo un aspetto negativo con questo metodo: dovrai ripetere la stessa "estensione" per tutti i metodi che rivelano l'elemento: show (), slideDown () ecc. È necessario qualcosa di più universale per risolvere questo problema per una volta e tutto, poiché è impossibile avere un evento "pronto" per delegate () o live ().
— Shahriyar Imanov,

1
Bene, l'unico problema è che la fadeTofunzione non funziona correttamente dopo l'implementazione di questa funzione
— Omid

9
Il tuo codice non sembra funzionare con l'ultimo jQuery (1.7.1 alla data di questo commento). Ho leggermente rielaborato questa soluzione per funzionare con l'ultimo jQuery: stackoverflow.com/a/9422207/135968
— mkmurray

1
Non riesco a far funzionare quel codice con la visibilità div attivata da una risposta Ajax.
— JackTheKnife,

Newbie qui. Non capisco perché obj (e newCallback) possano essere referenziati al di fuori della dichiarazione di function () come si può vedere in apply (). Mi è stato insegnato che dichiarare con var rende le variabili locali, ma rimuovere "var" le rende automaticamente globali.
— Yuta73,

85

Il problema viene affrontato dagli osservatori delle mutazioni DOM . Consentono di associare un osservatore (una funzione) a eventi che cambiano contenuto, testo o attributi di elementi dom.

Con il rilascio di IE11, tutti i principali browser supportano questa funzione, consultare http://caniuse.com/mutationobserver

Il codice di esempio è il seguente:

$(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
È un peccato che IE non lo supporti ancora. caniuse.com/mutationobserver -> Per vedere i browser che lo supportano.
— ccsakuweb,

2
Funziona davvero, e non ho bisogno di supportare i browser legacy, quindi è perfetto! I'br ha aggiunto una prova JSFiddle della risposta qui: jsfiddle.net/DanAtkinson/26URF
— Dan Atkinson,

funziona bene con Chrome ma non funziona con Blackberry 10 Cascade Webview (se a qualcun altro se ne frega;))
— Guillaume Gendre

1
Questo non sembra funzionare se la modifica della visibilità è causata da una modifica dell'attributo in un antenato di quella monitorata.
— Michael,

4
Questo non sembra funzionare in Chrome 51, non so perché. Eseguendo il codice sopra e premendo il pulsante, nessun avviso.
— Kris,

76

Non è possibile agganciare un evento nativo per questo, tuttavia è possibile attivare un evento dallo script dopo aver reso visibile il div utilizzando il. funzione trigger

per esempio

//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
La mia limitazione qui è che non ho necessariamente accesso al codice che mostra il mio div (). Quindi non sarei in grado di chiamare effettivamente il metodo trigger ().
— frankadelic,

11
Il JS è stato fornito da un team di sviluppo esterno alla mia organizzazione. È anche una specie di "scatola nera", quindi non vogliamo modificare quel codice se possibile. Potrebbe essere la nostra unica scelta però.
— frankadelic,

puoi sempre timbrare le loro funzioni js con la tua implementazione. Sembra triste!
— Redsquare

11
@redsquare: cosa succede se show()viene chiamato da più posizioni diverse dal blocco di codice discusso sopra?
— Robin Maben,

1
Per questo esempio, dovresti cambiare il nome della funzione in onIsVisibleperché l'uso di "isVisible" è un po 'ambiguo in questo momento.
— Brad Johnson,

27

È possibile utilizzare il plug-in Live Query di jQuery . E scrivi il codice come segue:

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

Quindi ogni volta che contentDiv è visibile, "fai qualcosa" verrà avvisato!


Bene dang, funziona. Ci avevo pensato e lo avevo respinto perché non funzionava, senza provarlo. Avrei dovuto provarlo. :)
— neminem,

1
Non ha funzionato per me. Viene visualizzato l'errore "livequery non è una funzione". Provato con "jquery-1.12.4.min.js" e "jquery-3.1.1.min.js"
— Paul Gorbas il

2
@Paul: è un plugin
— Christian il

Questo potrebbe rallentare notevolmente il tuo sito web! Il plug-in livequery esegue un polling rapido sugli attributi, anziché utilizzare metodi moderni ed efficienti come gli osservatori di mutazioni DOM. Quindi preferisco la soluzione di @hegemon: stackoverflow.com/a/16462443/19163 (e l'uso di polling solo come ripiego per le vecchie versioni di IE) - VOG 1 giorno fa
— VOG

20

La soluzione di redsquare è la risposta giusta.

Ma come soluzione IN-THEORY puoi scrivere una funzione che sta selezionando gli elementi classificati per .visibilityCheck( non tutti gli elementi visibili ) e controllare il loro visibilityvalore di proprietà; se truepoi fai qualcosa.

Successivamente, la funzione deve essere eseguita periodicamente utilizzando la setInterval()funzione. È possibile arrestare il timer utilizzando la clearInterval()corretta chiamata.

Ecco un esempio:

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

window.setInterval(foo, 100);

Puoi anche eseguire alcuni miglioramenti delle prestazioni su di esso, tuttavia, la soluzione è praticamente assurda da utilizzare in azione. Così...


5
non è una buona forma per usare una funzione implicita all'interno di setTimeout / setInterval. Usa setTimeout (foo, 100)
— redsquare il

1
Devo consegnartelo, è creativo, anche se brutto. Ed è stato abbastanza veloce e sporco da farmi il trucco in modo conforme a IE8. Grazie!
— JD Smith,

o display:none?
— Michael,

12

Il seguente codice (estratto da http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/ ) ti consentirà di utilizzare $('#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
Questo ha funzionato perfettamente per me, ma è importante notare che la funzione interrompe il concatenamento nello show e nasconde le funzioni, interrompendo molti plug-in. Aggiungi un ritorno davanti a el.apply(this, arguments)per risolvere questo problema.
— Jaimerump,

Questo è quello che stavo cercando! Il ritorno deve essere aggiunto come nel commento di @jaimerump
— Brainfeeder,

9

Se vuoi attivare l'evento su tutti gli elementi (e gli elementi figlio) che sono effettivamente resi visibili, da $ .show, toggle, toggleClass, addClass o 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;
    }
});

E ora il tuo elemento:

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

È possibile aggiungere sostituzioni a funzioni jQuery aggiuntive aggiungendole all'array in alto (come "attr")


9

un trigger di evento hide / show basato sull'ideea di Glenn: rimosso toggle perché attiva show / hide e non vogliamo 2fire per un evento

$(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
dovresti anche selezionare attr e removeAttr?
— Andrija,

5

Ho avuto lo stesso problema e ho creato un plugin jQuery per risolverlo per il nostro sito.

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

Ecco come lo useresti in base al tuo esempio:

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

1
Il polling non è molto efficiente e rallenterebbe in modo significativo il browser se fosse utilizzato per molti elementi.
— Cerin,


4

Ciò che mi ha aiutato qui è il recente polyfill delle specifiche ResizeObserver :

const divEl = $('#section60');

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

Nota che è crossbrowser e performant (nessun polling).


Ha funzionato bene per rilevare ogni volta che una riga della tabella veniva mostrata / nascosta a differenza di altre, inoltre un vantaggio è che non era richiesto alcun plug-in!
— BrettC

3

Ho fatto una semplice funzione setinterval per raggiungere questo obiettivo. Se l'elemento con la classe div1 è visibile, imposta div2 come visibile. Non conosco un buon metodo, ma una soluzione semplice.

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


2

Questo supporto facilita e attiva l'evento dopo l'animazione fatta! [testato su 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);

Ispirato da http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/


2

Basta associare un trigger al selettore e inserire il codice nell'evento trigger:

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

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

Penso che questa sia una soluzione abbastanza elegante. Ha funzionato per me.
— Software KIKO

1

È disponibile un plug-in jQuery per guardare il cambiamento negli attributi DOM,

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

Il plugin termina Tutto ciò che devi fare è associare MutationObserver

Puoi quindi usarlo per guardare il div usando:

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

1

Spero che questo farà il lavoro nel modo più semplice:

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

Ho modificato il trigger di evento Nascondi / Mostra da Catalint in base all'idea di Glenns. Il mio problema era che avevo un'applicazione modulare. Passo tra i moduli che mostrano e nascondono i genitori div. Quindi, quando nascondo un modulo e ne faccio vedere un altro, con il suo metodo ho un ritardo visibile quando cambio da un modulo all'altro. Ho solo bisogno di alleggerire questo evento e in alcuni bambini speciali. Così ho deciso di avvisare solo i bambini con la classe "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;
    }
});

Quindi quando un bambino vuole ascoltare l'evento "show" o "hide", devo aggiungerlo alla classe "displayObserver" e quando non vuole continuare ad ascoltarlo, rimuovo la classe

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

Desidero aiuto


0

Un modo per farlo.
Funziona solo sulle modifiche alla visibilità apportate dalla modifica della classe css, ma può essere estesa anche per controllare le modifiche agli attributi.

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

la mia soluzione:

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

esempio di utilizzo:

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

Questo controllo automatico del codice non richiede il controllo visibile o invisibile della query

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.