Gọi lại jQuery khi tải hình ảnh (ngay cả khi hình ảnh được lưu trữ)


291

Tôi muốn làm:

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

Nhưng sự kiện tải không kích hoạt khi hình ảnh được tải từ bộ đệm. Các tài liệu jQuery đề xuất một plugin để sửa lỗi này, nhưng nó không hoạt động


12
Vì câu hỏi của bạn đã thay đổi. Plugin bị hỏng đã được chuyển đến một ý chính và sau đó là repo với một plugin nhỏ hoạt động jQuery.imagesLoaded . Họ sửa tất cả các quirks trình duyệt nhỏ .
Lode

Thư viện JQuery đã đề cập ở trên chỉ hoạt động tốt với tôi. Cảm ơn!
plang

Cảm ơn bạn cho các plugin, nó hoạt động tốt.
onimojo

Câu trả lời:


572

Nếu srcđã được đặt, thì sự kiện sẽ kích hoạt trong trường hợp được lưu trong bộ nhớ cache, trước khi bạn thậm chí bị ràng buộc trình xử lý sự kiện. Để khắc phục điều này, bạn có thể lặp qua kiểm tra và kích hoạt sự kiện dựa trên .complete, như thế này:

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

Lưu ý thay đổi từ .bind()để .one()xử lý sự kiện không chạy hai lần.


8
Giải pháp của bạn hoạt động hoàn hảo với tôi nhưng tôi muốn hiểu điều gì đó, khi mã này "if (this.complete)" sẽ chạy, sau khi tải nội dung hình ảnh hay trước đó? bởi vì như tôi có thể hiểu từ .each này rằng bạn đang lặp trên tất cả $ ("img") và có thể nội dung hình ảnh trống và tải sẽ không xảy ra. hmmmm, tôi nghĩ rằng tôi còn thiếu một cái gì đó, thật tuyệt nếu bạn có thể mô tả rằng điều đó đang diễn ra để hiểu nó tốt hơn. cảm ơn.
Amr Elgarhy

33
Vì câu trả lời của bạn đã thay đổi. Hiện tại có một plugin hoạt động nhỏ jQuery.imagesLoaded . Họ sửa tất cả các quirks trình duyệt nhỏ .
Lode

3
Tôi sẽ thêm một setTimeout(function(){//do stuff},0)chức năng trong 'tải' ... 0 cung cấp thêm cho hệ thống trình duyệt (DOM) để đảm bảo mọi thứ được cập nhật chính xác.
DSdsdsdsd

14
@Lode - đó là một plugin HUGE, không hề nhỏ ở bất kỳ quy mô nào !!
vsync

5
vì phương thức JQuery 3 .load()không kích hoạt sự kiện và có 1 đối số bắt buộc, .trigger("load")thay vào đó , hãy sử dụng
ForceUser

48

Tôi có thể đề nghị bạn tải lại nó vào một đối tượng hình ảnh không phải DOM không? Nếu được lưu trong bộ nhớ cache, việc này sẽ không mất thời gian và tải sẽ vẫn kích hoạt. Nếu nó không được lưu trong bộ nhớ cache, nó sẽ kích hoạt tải khi hình ảnh được tải, cùng lúc với phiên bản DOM của hình ảnh kết thúc tải.

Javascript:

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

Đã cập nhật (để xử lý nhiều hình ảnh và với tệp đính kèm tải được sắp xếp chính xác):

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

1
Bạn nên thử đính kèm loadtrình xử lý trước khi nó được thêm vào, điều này cũng không hoạt động nhưng đối với một hình ảnh, mã OP hoạt động với nhiều người :)
Nick Craver

Cảm ơn, tôi đã thêm một phiên bản cập nhật mà tôi hy vọng giải quyết hai vấn đề này.
Gus

#imglà một ID không phải là một bộ chọn phần tử :) Cũng this.srchoạt động, không cần sử dụng jQuery khi không cần thiết :) Nhưng việc tạo một hình ảnh khác có vẻ như quá mức cần thiết trong cả hai trường hợp IMO.
Nick Craver

nó phải là $ ('img'). nhưng giải pháp là gr8 trong 2k15 cũng được
Dimag Kharab

Ngoài ra, đối với trường hợp nhiều hình ảnh, nếu bạn muốn hoạt động trên phần tử DOM cho mỗi hình ảnh trong imageLoadedđó, hãy sử dụngtmp.onload = imageLoaded.bind(this)
blisstdev

38

Giải pháp đơn giản của tôi, nó không cần bất kỳ plugin bên ngoài nào và đối với các trường hợp phổ biến là đủ:

/**
 * 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);
            });
        }
    });
};

sử dụng nó như thế này:

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

ví dụ, để làm mờ dần hình ảnh của bạn khi tải, bạn có thể làm:

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

Hoặc thay thế, nếu bạn thích một cách tiếp cận giống như 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);

và sử dụng nó theo cách này:

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

ví dụ:

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

urm .. và nếu hình ảnh của tôi có kích thước được đặt với css thì sao?
Simon_Weaver

Set width, max-heightvà nghỉ height:auto, thường đó là cần thiết để thiết lập cả chiều rộng và chiều cao chỉ khi bạn cần phải căng hình ảnh; để có giải pháp mạnh mẽ hơn, tôi sẽ sử dụng một plugin như ImagesLoaded
guari

1
vâng, đó là một sự nhầm lẫn, jsdoc vẫn ổn, tôi đã sửa lại ví dụ dòng
guari

1
Giải pháp này với $ .fn.imgLoad hoạt động trên các trình duyệt. Việc sửa nó sẽ còn tốt hơn nữa: && / * cho IE 10 - * / this.naturalHeight> 0 Dòng này sẽ không xem xét kiểu css vì img có thể bị chặn nhưng có chiều cao. Làm việc tại IE10
Patryk Padus

1
Điều này không có một điều kiện chủng tộc (nhỏ). Hình ảnh có thể được tải trong một luồng khác vào tập lệnh và do đó có thể tải giữa kiểm tra hoàn thành và đính kèm sự kiện onload, trong trường hợp đó sẽ không có gì xảy ra. Thông thường, JS là đồng bộ với kết xuất và vì vậy bạn không phải lo lắng về điều kiện cuộc đua nhưng đây là một ngoại lệ. html.spec.whatwg.org/multipage/ Kẻ
Mog0

23

Bạn có thực sự phải làm điều đó với jQuery không? Bạn cũng có thể đính kèm onloadsự kiện trực tiếp vào hình ảnh của mình;

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

Nó sẽ kích hoạt mỗi khi hình ảnh được tải, từ bộ đệm hay không.


5
Nó sạch hơn mà không cần javascript nội tuyến
hazelnut

1
Xin chào, Bjorn, onloadtrình xử lý sẽ kích hoạt khi hình ảnh được tải lần thứ hai từ bộ đệm?
SexyBeast

1
@Coolvogel không nó sẽ không.
Quảng cáo

3
Anh bạn, tôi đã cứu mạng tôi, tôi đã thử ít nhất một tá giải pháp khác và bạn là giải pháp duy nhất thực sự hiệu quả. Tôi nghiêm túc suy nghĩ về việc tạo thêm 10 tài khoản để bình chọn câu trả lời của bạn thêm 10 lần nữa. Xin chân thành cảm ơn!
Carlo

1
Tôi chưa bao giờ hiểu ác cảm của mọi người đối với JS nội tuyến. Tải hình ảnh xảy ra riêng biệt và ngoài chuỗi từ phần còn lại của quá trình tải trang. Do đó, đây là giải pháp hợp lệ duy nhất để nhận phản hồi ngay lập tức về việc hoàn thành tải hình ảnh. Tuy nhiên, nếu ai đó cần đợi jQuery tải và điều đó xảy ra sau đó, thì họ sẽ không phải sử dụng giải pháp đơn giản ở đây và sử dụng một trong những giải pháp khác (Piotr có lẽ là lựa chọn tốt nhất vì nó không phụ thuộc vào giả hack nhưng kiểm tra chiều cao () của guari cũng có thể quan trọng). Một hàng đợi xử lý cũng có thể hữu ích.
CubicleSoft 17/03/2017

16

Bạn cũng có thể sử dụng mã này với sự hỗ trợ cho lỗi tải:

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

1
Điều này làm việc tốt nhất cho tôi. Tôi nghĩ rằng chìa khóa là để thiết lập loadtrình xử lý trước và sau đó gọi nó sau trong eachchức năng.
GreenRaccoon23

cài đặt hình ảnh bằng mã bên dưới '' var img = new Image (); img.src = sosImgURL; $ (img) .on ("tải", hàm () {''
imdadhusen

13

Tôi chỉ gặp vấn đề này một mình, tìm kiếm mọi giải pháp không liên quan đến việc giết bộ nhớ cache hoặc tải xuống plugin.

Tôi đã không thấy chủ đề này ngay lập tức vì vậy tôi đã tìm thấy một cái gì đó khác thay vào đó là một sửa chữa thú vị và (tôi nghĩ) xứng đáng để đăng ở đây:

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

Tôi thực sự có ý tưởng này từ các ý kiến ​​ở đây: http://www.witheringtree.com/2009/05/image-load-event-binding-with-ie-USE-jquery/

Tôi không biết tại sao nó hoạt động nhưng tôi đã thử nghiệm điều này trên IE7 và nó đã bị hỏng trước khi nó hoạt động.

Hy vọng nó giúp,

Biên tập

Câu trả lời được chấp nhận thực sự giải thích tại sao:

Nếu src đã được đặt thì sự kiện sẽ kích hoạt trong bộ đệm được đặt trước khi bạn xử lý ràng buộc sự kiện.


4
Tôi đã thử nghiệm điều này ở rất nhiều trình duyệt và không thấy nó bị lỗi ở bất cứ đâu. Tôi đã sử dụng $image.load(function(){ something(); }).attr('src', $image.attr('src'));để thiết lập lại nguồn ban đầu.
Maurice

Tôi đã thấy rằng phương pháp này không hoạt động trên iOS, ít nhất là đối với Safari / 601.1 và Safari / 602.1
cronfy

Sẽ là tuyệt vời để tìm hiểu tại sao, nó là trình duyệt nào?
Sammaye

5

Bằng cách sử dụng jQuery để tạo một hình ảnh mới với src của hình ảnh và gán phương thức tải trực tiếp vào đó, phương thức tải được gọi thành công khi jQuery hoàn thành việc tạo hình ảnh mới. Điều này làm việc cho tôi trong IE 8, 9 và 10

$('<img />', {
    "src": $("#img").attr("src")
}).load(function(){
    // Do something
});

Đối với những người sử dụng Ruby on Rails, với yêu cầu từ xa, sử dụng các mẫu __rjs.erb, đây là giải pháp duy nhất tôi tìm thấy. Bởi vì sử dụng partials trong js.erb, nó mất đi sự ràng buộc ($ ("# img" .on ("load") ... và đối với hình ảnh thì KHÔNG thể ủy thác phương thức "on": $ ("body"). bật ("tải", "# img", hàm () ...
Albert Català

5

Một giải pháp tôi tìm thấy https://bugs.chromium.org/p/chromium/issues/detail?id=7731#c12 (Mã này được lấy trực tiếp từ nhận xét)

var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;

Điều này thật đúng với gì mà tôi đã tìm kiếm. Một hình ảnh duy nhất đến với tôi từ máy chủ. Tôi muốn tải trước nó và sau đó phục vụ. Không chọn tất cả các hình ảnh như nhiều câu trả lời làm. Tốt một.
Kai Qing

4

Một sửa đổi cho ví dụ của GUS:

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

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

Đặt nguồn trước và sau khi tải.


Giống như câu trả lời của @ Gus, điều này sẽ không hoạt động đối với nhiều hình ảnh ... và không cần thiết lập srctrước khi đính kèm onloadtrình xử lý, một lần sau đó sẽ đủ.
Nick Craver

3

Chỉ cần thêm lại đối số src trên một dòng riêng sau khi xác định oject img. Điều này sẽ lừa IE kích hoạt sự kiện lad. Nó là xấu, nhưng đó là cách giải quyết đơn giản nhất mà tôi tìm thấy cho đến nay.

jQuery('<img/>', {
    src: url,
    id: 'whatever'
})
.load(function() {
})
.appendTo('#someelement');
$('#whatever').attr('src', url); // trigger .load on IE

1

Tôi có thể cho bạn một lời khuyên nhỏ nếu bạn muốn làm như thế này:

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

Nếu bạn làm điều đó khi trình duyệt lưu trữ hình ảnh, không có vấn đề gì luôn luôn hiển thị img nhưng tải img dưới hình ảnh thực.


1

Tôi đã gặp vấn đề này với IE trong đó e.target. Thong sẽ không được xác định. Sự kiện tải sẽ kích hoạt nhưng tôi không thể lấy kích thước của hình ảnh trong IE (chrome + FF hoạt động).

Hóa ra bạn cần tìm e.cienTarget.naturalWidth & e.cienTarget.naturalHeight .

Một lần nữa, IE thực hiện mọi thứ theo cách riêng (phức tạp hơn).


0

Bạn có thể giải quyết vấn đề của mình bằng cách sử dụng plugin JAIL cũng cho phép bạn lười tải hình ảnh (cải thiện hiệu suất trang) và chuyển cuộc gọi lại dưới dạng tham số

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

HTML sẽ giống như

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

0

Nếu bạn muốn một giải pháp CSS thuần túy, thủ thuật này hoạt động rất tốt - sử dụng đối tượng biến đổi. Điều này cũng hoạt động với hình ảnh khi chúng được lưu trữ hay không:

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

Ví dụ Codepen

Ví dụ về Codepen


Bạn có thể cho một ví dụ về cách thức này hoạt động với tải hình ảnh?
Hastig Zusammenstellen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.