Rappel jQuery lors du chargement de l'image (même lorsque l'image est mise en cache)

291

Je veux faire:

$("img").bind('load', function() {
  // do stuff
});

Mais l'événement de chargement ne se déclenche pas lorsque l'image est chargée à partir du cache. Les documents jQuery suggèrent un plugin pour résoudre ce problème, mais cela ne fonctionne pas

Tom Lehman
la source
12
Depuis votre question, les choses ont changé. Le plugin cassé a été déplacé dans un gist et plus tard dans un dépôt avec un petit plugin de travail jQuery.imagesLoaded . Ils corrigent toutes les petites bizarreries du navigateur .
Lode
La bibliothèque JQuery mentionnée ci-dessus a très bien fonctionné pour moi. Merci!
plang
Merci pour le plugin, cela a bien fonctionné.
onimojo

Réponses:

572

Si le srcest déjà défini, l'événement se déclenche dans le cas mis en cache, avant même que le gestionnaire d'événements ne soit lié. Pour résoudre ce problème, vous pouvez parcourir en boucle la vérification et le déclenchement de l'événement en fonction .complete, comme ceci:

$("img").one("load", function() {
  // do stuff
}).each(function() {
  if(this.complete) {
      $(this).load(); // For jQuery < 3.0 
      // $(this).trigger('load'); // For jQuery >= 3.0 
  }
});

Notez la modification de .bind()à .one()afin que le gestionnaire d'événements ne s'exécute pas deux fois.

Nick Craver
la source
8
Votre solution a fonctionné parfaitement pour moi mais je veux comprendre quelque chose, quand ce code "si (this.complete)" s'exécutera, après le chargement du contenu de l'image ou avant? car comme je peux comprendre de cela .chaque que vous faites une boucle sur tous les $ ("img") et que le contenu de l'image soit vide et que la charge ne se produira pas. hmmmm, je pense qu'il me manque quelque chose, ce sera bien si vous pouvez décrire ce qui se passe pour mieux le comprendre. Merci.
Amr Elgarhy
33
Depuis votre réponse, les choses ont changé. Il y a maintenant un petit plugin fonctionnel jQuery.imagesLoaded . Ils corrigent toutes les petites bizarreries du navigateur .
Lode
3
J'ajouterais un setTimeout(function(){//do stuff},0)dans la fonction 'charger' ... le 0 donne au système de navigateur (DOM) une coche supplémentaire pour s'assurer que tout est correctement mis à jour.
dsdsdsdsd
14
@Lode - c'est un plugin ÉNORME, pas petit du tout à n'importe quelle échelle !!
vsync
5
puisque la méthode JQuery 3 .load()ne déclenche pas d'événement et a 1 argument requis, utilisez .trigger("load")plutôt
ForceUser
48

Puis-je vous suggérer de le recharger dans un objet image non DOM? S'il est mis en cache, cela ne prendra pas de temps du tout et la charge continuera de se déclencher. S'il n'est pas mis en cache, il déclenchera la onload lors du chargement de l'image, ce qui devrait être en même temps que la version DOM de l'image termine le chargement.

Javascript:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.src = $('#img').attr('src') ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;
}) ;

Mise à jour (pour gérer plusieurs images et avec une pièce jointe correctement commandée):

$(document).ready(function() {
    var imageLoaded = function() {
        // Run onload code.
    }
    $('#img').each(function() {
        var tmpImg = new Image() ;
        tmpImg.onload = imageLoaded ;
        tmpImg.src = $(this).attr('src') ;
    }) ;
}) ;
Gus
la source
1
Vous devriez essayer d'attacher le loadgestionnaire avant qu'il ne soit ajouté, cela ne fonctionnera pas non plus, mais pour une image, le code OP fonctionne pour beaucoup :)
Nick Craver
Merci, j'ai ajouté une version mise à jour qui, je l'espère, résout ces deux problèmes.
Gus
#imgest un ID pas un sélecteur d'élément :) Fonctionne également this.src, pas besoin d'utiliser jQuery là où il n'est pas nécessaire :) Mais créer une autre image semble exagéré dans les deux cas IMO.
Nick Craver
ce devrait être $ ('img'). mais la solution est gr8 en 2k15 aussi
Dimag Kharab
En outre, pour le cas d'images multiples, si vous souhaitez opérer sur l'élément DOM pour chaque image imageLoaded, utilisez alorstmp.onload = imageLoaded.bind(this)
blisstdev
38

Ma solution simple, elle n'a besoin d'aucun plugin externe et pour les cas courants devrait suffire:

/**
 * Trigger a callback when the selected images are loaded:
 * @param {String} selector
 * @param {Function} callback
  */
var onImgLoad = function(selector, callback){
    $(selector).each(function(){
        if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
            callback.apply(this);
        }
        else {
            $(this).on('load', function(){
                callback.apply(this);
            });
        }
    });
};

utilisez-le comme ceci:

onImgLoad('img', function(){
    // do stuff
});

par exemple, pour estomper vos images au chargement, vous pouvez faire:

$('img').hide();
onImgLoad('img', function(){
    $(this).fadeIn(700);
});

Ou comme alternative, si vous préférez une approche de type plugin jquery:

/**
 * Trigger a callback when 'this' image is loaded:
 * @param {Function} callback
 */
(function($){
    $.fn.imgLoad = function(callback) {
        return this.each(function() {
            if (callback) {
                if (this.complete || /*for IE 10-*/ $(this).height() > 0) {
                    callback.apply(this);
                }
                else {
                    $(this).on('load', function(){
                        callback.apply(this);
                    });
                }
            }
        });
    };
})(jQuery);

et utilisez-le de cette manière:

$('img').imgLoad(function(){
    // do stuff
});

par exemple:

$('img').hide().imgLoad(function(){
    $(this).fadeIn(700);
});
guari
la source
urm .. et si mes images ont une taille définie avec css?
Simon_Weaver
Ensemble width, max-heightet les congés height:auto, il est souvent nécessaire de définir à la fois que la largeur et la hauteur si vous avez besoin d'étirer l'image; pour une solution plus robuste, j'utiliserais un plugin comme ImagesLoaded
guari
1
oui c'était une erreur de frappe, jsdoc était ok, j'ai corrigé l'exemple de ligne
guari
1
Cette solution avec $ .fn.imgLoad fonctionne sur tous les navigateurs. Le corriger sera encore mieux: && / * pour IE 10 - * / this.naturalHeight> 0 Cette ligne ne prendra pas en compte le style CSS car img peut être bloqué mais avoir de la hauteur. Travailler dans IE10
Patryk Padus
1
Cela a une (petite) condition de concurrence. L'image peut être chargée dans un thread différent du script et peut donc se charger entre la vérification de la fin et la connexion de l'événement onload, auquel cas rien ne se passera. Normalement, JS est synchrone avec le rendu et vous n'avez donc pas à vous soucier des conditions de course, mais c'est une exception. html.spec.whatwg.org/multipage/…
Mog0
23

Devez-vous vraiment le faire avec jQuery? Vous pouvez également joindre l' onloadévénement directement à votre image;

<img src="/path/to/image.jpg" onload="doStuff(this);" />

Il se déclenchera à chaque chargement de l'image, depuis le cache ou non.

Björn
la source
5
Il est plus propre sans javascript en ligne
noisette
1
Salut, Bjorn, le onloadgestionnaire se déclenchera-t-il lorsque l'image sera chargée une deuxième fois depuis le cache?
SexyBeast
1
@Cupidvogel non, ce ne sera pas le cas.
Annonces
3
Mec, tu m'as sauvé la vie, j'ai essayé au moins une douzaine d'autres solutions et la tienne est la seule qui a vraiment fonctionné. J'envisage sérieusement de créer 10 comptes supplémentaires pour voter votre réponse 10 fois de plus. Sérieusement merci!
Carlo
1
Je n'ai jamais compris l'aversion des gens pour JS en ligne. Le chargement d'image se produit séparément et hors séquence par rapport au reste du processus de chargement de page. Par conséquent, c'est la seule solution valable pour obtenir un retour immédiat sur la fin du chargement de l'image. Cependant, si quelqu'un doit attendre que jQuery se charge et que cela se produise plus tard, alors il ne devra pas utiliser la solution simple ici et utiliser l'une des autres solutions (celle de Piotr est probablement la meilleure option car elle ne dépend pas de pseudo- hacks mais la vérification de la hauteur () de guari peut aussi être importante). Une file d'attente de traitement pourrait également être utile.
CubicleSoft
16

Vous pouvez également utiliser ce code avec la prise en charge des erreurs de chargement:

$("img").on('load', function() {
  // do stuff on success
})
.on('error', function() {
  // do stuff on smth wrong (error 404, etc.)
})
.each(function() {
    if(this.complete) {
      $(this).load();
    } else if(this.error) {
      $(this).error();
    }
});
Piotr Stępniewski
la source
1
Celui-ci a fonctionné le mieux pour moi. Je pense que la clé est de définir d'abord le loadgestionnaire, puis de l'appeler plus tard dans la eachfonction.
GreenRaccoon23
définition de l'image à l'aide du code ci-dessous '' var img = new Image (); img.src = sosImgURL; $ (img) .on ("load", function () {''
imdadhusen
13

Je viens d'avoir ce problème moi-même, j'ai cherché partout une solution qui n'impliquait pas de tuer mon cache ou de télécharger un plugin.

Je n'ai pas vu ce fil immédiatement, j'ai donc trouvé autre chose à la place qui est une solution intéressante et (je pense) digne d'être publiée ici:

$('.image').load(function(){
    // stuff
}).attr('src', 'new_src');

J'ai en fait eu cette idée dans les commentaires ici: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-using-jquery/

Je ne sais pas pourquoi cela fonctionne, mais j'ai testé cela sur IE7 et où il s'est cassé avant qu'il ne fonctionne maintenant.

J'espère que ça aide,

Éditer

La réponse acceptée explique en fait pourquoi:

Si le src est déjà défini, l'événement se déclenche dans le cache mis en cache avant que le gestionnaire d'événements ne soit lié.

Sammaye
la source
4
J'ai testé cela dans de nombreux navigateurs et je n'ai trouvé aucun échec. J'ai utilisé $image.load(function(){ something(); }).attr('src', $image.attr('src'));pour réinitialiser la source d'origine.
Maurice
J'ai trouvé que cette approche ne fonctionne pas sur iOS, au moins pour Safari / 601.1 et Safari / 602.1
cronfy
Ce serait génial de savoir pourquoi, dans quel navigateur se trouve-t-il?
Sammaye
5

En utilisant jQuery pour générer une nouvelle image avec le src de l'image et en lui affectant directement la méthode de chargement, la méthode de chargement est appelée avec succès lorsque jQuery a fini de générer la nouvelle image. Cela fonctionne pour moi dans IE 8, 9 et 10

$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});
pseudo
la source
Pour ceux qui utilisent Ruby on Rails, avec une demande à distance, en utilisant des modèles __rjs.erb, c'est la seule solution que j'ai trouvée. Parce que l'utilisation de partiels dans js.erb, il perd la liaison ($ ("# img" .on ("load") ... et pour les images n'est PAS possible de déléguer la méthode "on": $ ("body"). on ("load", "# img", function () ...
Albert Català
5

Une solution que j'ai trouvée https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (Ce code provient directement du commentaire)

var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;
AllisonC
la source
Ceci est exactement ce que je cherchais. Une seule image me vient du serveur. Je voulais le précharger puis le servir. Ne pas sélectionner toutes les images comme le font de nombreuses réponses. Bon.
Kai Qing
4

Une modification de l'exemple de GUS:

$(document).ready(function() {
    var tmpImg = new Image() ;
    tmpImg.onload = function() {
        // Run onload code.
    } ;

tmpImg.src = $('#img').attr('src');
})

Définissez la source avant et après l'onload.

Chuck Conway
la source
Comme la réponse de @ Gus, cela ne fonctionnera pas pour plusieurs images ... et il n'est pas nécessaire de définir srcavant l'attachement du onloadgestionnaire, une fois après suffira.
Nick Craver
3

Il suffit de rajouter l'argument src sur une ligne distincte après la définition de l'objet img. Cela incitera IE à déclencher l'événement lad. C'est moche, mais c'est la solution de contournement la plus simple que j'ai trouvée jusqu'à présent.

jQuery('<img/>', {
    src: url,
    id: 'whatever'
})
.load(function() {
})
.appendTo('#someelement');
$('#whatever').attr('src', url); // trigger .load on IE
Bjørn T. Dahl
la source
1

Je peux vous donner un petit conseil si vous voulez faire comme ça:

<div style="position:relative;width:100px;height:100px">
     <img src="loading.jpg" style='position:absolute;width:100px;height:100px;z-index:0'/>
     <img onLoad="$(this).fadeIn('normal').siblings('img').fadeOut('normal')" src="picture.jpg" style="display:none;position:absolute;width:100px;height:100px;z-index:1"/>
</div>

Si vous faites cela lorsque le navigateur met en cache des images, ce n'est pas un problème, toujours img affiché, mais en chargeant img sous une image réelle.

Josephy Besim
la source
1

J'ai eu ce problème avec IE où l'e.target.width ne serait pas défini. L'événement de chargement se déclencherait mais je n'ai pas pu obtenir les dimensions de l'image dans IE (chrome + FF a fonctionné).

Il se trouve que vous devez chercher e.currentTarget.naturalWidth & e.currentTarget.naturalHeight .

Encore une fois, IE fait les choses à sa façon (plus compliquée).

Ali
la source
0

Vous pouvez résoudre votre problème en utilisant le plugin JAIL qui vous permet également de charger des images paresseuses (améliorant les performances de la page) et de passer le rappel en paramètre

$('img').asynchImageLoader({callback : function(){...}});

Le HTML devrait ressembler à

<img name="/global/images/sample1.jpg" src="/global/images/blank.gif" width="width" height="height" />
sebarmeli
la source
0

Si vous voulez une solution CSS pure, cette astuce fonctionne très bien - utilisez l'objet transform. Cela fonctionne également avec les images lorsqu'elles sont mises en cache ou non:

CSS:

.main_container{
    position: relative;
    width: 500px;
    height: 300px;
    background-color: #cccccc;
}

.center_horizontally{
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: green;
  left: 50%;
  top: 0;
  transform: translate(-50%,0);
}

.center_vertically{
  position: absolute;
  top: 50%;
  left: 0;
  width: 100px;
  height: 100px;
  background-color: blue;
  transform: translate(0,-50%);
}

.center{
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100px;
  height: 100px;
  background-color: red;
  transform: translate(-50%,-50%);
}

HTML:

<div class="main_container">
  <div class="center_horizontally"></div>
  <div class="center_vertically"></div>
  <div class="center"></div>
  </div>
</div

Exemple de Codepen

Exemple Codepen LESS

neoRiley
la source
Pourriez-vous donner un exemple de la façon dont cela fonctionne avec le chargement d'images?
Hastig Zusammenstellen