Как с помощью Javascript / jQuery определить, загрузилось ли изображение?

85

Я пишу Javascript, чтобы изменить размер большого изображения, чтобы оно поместилось в окне браузера пользователя. (К сожалению, я не контролирую размер исходных изображений.)

В HTML будет что-то вроде этого:

<img id="photo"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Есть ли способ определить, было ли загружено srcизображение в imgтеге?

Мне это нужно, потому что я сталкиваюсь с проблемой, если $(document).ready()выполняется до того, как браузер загрузит изображение. $("#photo").width()и $("#photo").height()вернет размер заполнителя (альтернативный текст). В моем случае это примерно 134 х 20.

Прямо сейчас я просто проверяю, меньше ли высота фотографии 150, и предполагаю, что если да, то это просто замещающий текст. Но это настоящий взлом, и он сломается, если высота фотографии меньше 150 пикселей (маловероятно в моем конкретном случае) или если высота альтернативного текста больше 150 пикселей (возможно, это произойдет в небольшом окне браузера) .


Изменить: для всех, кто хочет увидеть код:

$(function()
{
  var REAL_WIDTH = $("#photo").width();
  var REAL_HEIGHT = $("#photo").height();

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(REAL_HEIGHT < 150)
    {
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
      if(REAL_HEIGHT < 150)
      {
        //image not loaded.. try again in a quarter-second
        setTimeout(adjust_photo_size, 250);
        return;
      }
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});

Обновление : спасибо за предложения. Существует риск того, что событие не будет запущено, если я установлю обратный вызов для $("#photo").loadсобытия, поэтому я определил событие onLoad непосредственно в теге изображения. Для записи вот код, который я выбрал:

<img id="photo"
     onload="photoLoaded();"
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />

Затем в Javascript:

//This must be outside $() because it may get called first
var isPhotoLoaded = false;
function photoLoaded()
{
  isPhotoLoaded = true;
}

$(function()
{
  //Hides scrollbars, so we can resize properly.  Set with JS instead of
  //  CSS so that page doesn't break with JS disabled.
  $("body").css("overflow", "hidden");

  var REAL_WIDTH = -1;
  var REAL_HEIGHT = -1;

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()
  {
    if(!isPhotoLoaded)
    {
      //image not loaded.. try again in a quarter-second
      setTimeout(adjust_photo_size, 250);
      return;
    }
    else if(REAL_WIDTH < 0)
    {
      //first time in this function since photo loaded
      REAL_WIDTH = $("#photo").width();
      REAL_HEIGHT = $("#photo").height();
    }

    var new_width = . . . ;
    var new_height = . . . ;

    $("#photo").width(Math.round(new_width));
    $("#photo").height(Math.round(new_height));
  }

});
Кип
источник
1
Это так давно, и вы уже приняли ответ, поэтому я просто прокомментирую здесь. Почему вы не можете использовать плагин jQuery onImagesLoad? А также, jQuery или нет, что не так с настройкой max-widthи max-heightстилем изображения с помощью Javascript? Затем вы избегаете ВСЕГО кода, который вам нужно написать, устанавливая максимальную ширину / высоту равной размеру ширины / высоты области просмотра.
@ jon.wd7: я не знаком с этим плагином, и, возможно, его еще не было в 2008 году. что касается max-width и max-height, две вещи: 1) они не поддерживаются в IE (ну, я не уверен насчет IE 8); 2) если окно просмотра меняет размер (размер окна изменяется и т. Д.), Мне все равно понадобится javascript для изменения max-width / max-height (хотя я могу этого не делать, если бы я использовал «100%», а не измерение в пикселях)
Кип
Согласно quirksmode.org, он определенно поддерживается в IE7 и IE8. Так что я не уверен в вашей проблеме. Я лично не поддерживаю IE6 для подобных мелочей, поэтому они увидят то же самое, что увидит пользователь без включенного JS. И, конечно, вам нужно будет сбросить max-widthи max-heightизменить размер. Но это довольно простая задача, на .resize()самом деле однострочное использование .
3
Свойство complete дает обязательный способ узнать, загружено ли изображение.
Борис Окунский
1
@Adrien, Mozilla теперь показывает полную, поскольку поддерживается всеми основными браузерами, кроме Andoid (неизвестно).
Оливер Бок

Ответы:

33

Либо добавьте прослушиватель событий, либо заставьте изображение объявить о себе с помощью onload. Затем определите размеры оттуда.

<img id="photo"
     onload='loaded(this.id)'
     src="a_really_big_file.jpg"
     alt="this is some alt text"
     title="this is some title text" />
Диодей - Джеймс Макфарлейн
источник
Согласно спецификации, onload - это только событие для элементов body и frameset; Тем не менее, я думаю, что это работает в IE, но я не знаю, работает ли это в каких-либо других браузерах или что это то, что вы можете предположить ... Я только что изучал это пару недель назад ...
Джейсон Бантинг
Я тестировал его в IE6, IE7, FF3, Opera9, Safair (Windows Beta) и Chrome (Windows Beta).
Кип
9
Беспорядочное поведение и контент не приветствуются. События следует применять с использованием Javascript, а не HTML.
dionyziz
10
Его комментарий актуален, поскольку мы не застряли в 2008 году. Люди все еще читают это, и даже если это не считалось плохой практикой в ​​08, вы не хотите, чтобы люди думали, что это хорошая практика сейчас.
Spoeken 05
3
document.querySelector ("img"). addEventListener ("load", function () {alert ('onload!');});
Франк Швитерман
14

Используя хранилище данных jquery, вы можете определить состояние «загружено».

<img id="myimage" onload="$(this).data('loaded', 'loaded');" src="lolcats.jpg" />

Тогда в другом месте вы можете сделать:

if ($('#myimage').data('loaded')) {
    // loaded, so do stuff
}
Майк Фогель
источник
2
Используйте jQuery(this)вместо этого $(this)для лучшей совместимости с другими библиотеками (jquery не владеет $), особенно если у вас есть JavaScript в html.
Джон Магнолия
11

Правильный ответ - использовать event.special.load

Возможно, что событие загрузки не сработает, если изображение загружается из кеша браузера. Чтобы учесть эту возможность, мы можем использовать специальное событие загрузки, которое запускается немедленно, если изображение готово. event.special.load в настоящее время доступен как плагин.

Согласно документации по .load ()

Эван Кэрролл
источник
2
Я собирался добавить ответ с таким предложением, рад, что увидел этот ответ, прежде чем его кодировать. По сути, ваши обработчики должны сначала проверить, загружено ли изображение, прежде чем устанавливать обработчик. Если он уже загружен, немедленно активируйте обратный вызов. Это то, что $.readyделает. Я собирался предложить просто проверить ширину, но в этом есть свои проблемы, мне очень нравится это решение.
Хуан Мендес,
5

Вы хотите сделать то, что сказал Аллен, однако имейте в виду, что иногда изображение загружается до готовности dom, что означает, что ваш обработчик нагрузки не срабатывает. Лучше всего сделать так, как говорит Аллен, но установить src изображения с помощью javascript после присоединения обработчика нагрузки. Таким образом, вы можете гарантировать, что он сработает.

Что касается доступности, будет ли ваш сайт по-прежнему работать для людей без javascript? Вы можете указать тегу img правильный src, прикрепить обработчик dom ready для запуска вашего js: очистить изображение src (установить фиксированное значение и высоту с помощью css, чтобы предотвратить мерцание страницы), затем установите свой обработчик загрузки img, затем сбросьте src на правильный файл. Таким образом вы охватите все базы :)

Эндрю Баллок
источник
Сайт по-прежнему работает для людей без Javascript, им просто нужно прокрутить, чтобы увидеть все изображение.
Кип
4

Согласно одному из недавних комментариев к вашему исходному вопросу

$(function() {

  $(window).resize(adjust_photo_size);
  adjust_photo_size();

  function adjust_photo_size()  {
    if (!$("#photo").get(0).complete) {
       $("#photo").load(function() {
          adjust_photo_size();
       });
    } else {
      ... 
    }
});

Предупреждение. Этот ответ может вызвать серьезный цикл в IE8 и ниже, потому что img.complete не всегда правильно устанавливается браузером. Если вы должны поддерживать ie8, используйте флаг, чтобы помнить, что изображение загружено.

обыкновенная пика
источник
Погоди. Этот ответ может вызвать серьезный цикл в IE8 и ниже, потому что img.complete не всегда правильно устанавливается браузером. Если вы должны поддерживать ie8, используйте флаг, чтобы помнить, что изображение загружено.
commonpike
2

Попробуйте что-нибудь вроде:

$("#photo").load(function() {
    alert("Hello from Image");
});
Аллен Лалонд
источник
6
Спасибо. Однако существует вероятность того, что изображение уже было загружено до того, как это произойдет, и в этом случае событие загрузки не будет запущено.
Кип
Как насчет тега сценария, устанавливающего это событие сразу после тега изображения? Причина использования ready - убедиться, что загружен весь документ, что в данном случае не нужно, верно?
Джейсон Гоэмаат
2

Есть плагин jQuery под названием «imagesLoaded», который предоставляет кроссбраузерный метод проверки, загружены ли изображения элемента.

Сайт: https://github.com/desandro/imagesloaded/

Использование для контейнера, внутри которого много изображений:

$('container').imagesLoaded(function(){
 console.log("I loaded!");
})

Плагин отличный:

  1. работает для проверки контейнера с множеством изображений внутри
  2. работает для проверки img, чтобы увидеть, загрузился ли он
сойка
источник
кажется довольно тяжелым 5 КБ, уменьшенным только для проверки загруженного img. Должен быть доступный более легкий метод ...
cdalxndr
1

Есть комментарии по этому поводу?

...

doShow = function(){
  if($('#img_id').attr('complete')){
    alert('Image is loaded!');
  } else {
    window.setTimeout('doShow()',100);
  }
};

$('#img_id').attr('src','image.jpg');

doShow();

...

Вроде везде работает ...

Джей
источник
это плохая практикаsetTimeout
cdalxndr
1

Я только что создал функцию jQuery для загрузки изображения с помощью отложенного объекта jQuerys, что позволяет очень легко реагировать на событие загрузки / ошибки:

$.fn.extend({
    loadImg: function(url, timeout) {
        // init deferred object
        var defer = $.Deferred(),
            $img = this,
            img = $img.get(0),
            timer = null;

        // define load and error events BEFORE setting the src
        // otherwise IE might fire the event before listening to it
        $img.load(function(e) {
            var that = this;
            // defer this check in order to let IE catch the right image size
            window.setTimeout(function() {
                // make sure the width and height are > 0
                ((that.width > 0 && that.height > 0) ? 
                    defer.resolveWith : 
                    defer.rejectWith)($img);
            }, 1);
        }).error(function(e) {
            defer.rejectWith($img);
        });

        // start loading the image
        img.src = url;

        // check if it's already in the cache
        if (img.complete) {
            defer.resolveWith($img);
        } else if (0 !== timeout) {
            // add a timeout, by default 15 seconds
            timer = window.setTimeout(function() {
                defer.rejectWith($img);
            }, timeout || 15000);
        }

        // return the promise of the deferred object
        return defer.promise().always(function() {
            // stop the timeout timer
            window.clearTimeout(timer);
            timer = null;
            // unbind the load and error event
            this.off("load error");
        });
    }
});

Применение:

var image = $('<img />').loadImg('http://www.google.com/intl/en_com/images/srpr/logo3w.png')
.done(function() {
    alert('image loaded');
    $('body').append(this);
}).fail(function(){
    alert('image failed');
});

Посмотрите, как это работает: http://jsfiddle.net/roberkules/AdWZj/

Роберкулес
источник
Звучит замечательно. Не могли бы вы привести точный пример того, когда ваш код предпочтительнее img.onload и img.onerror? Ваш код предназначен только для обработки кажущихся недостатков обработки ошибок jQuery, о которых я только что был проинформирован: эта ошибка не запускается в IE, если нет расширения файла?
mplungjan
интересная идея. так что зная, когда изображение сделано, это легко. Но что, если изображение не может быть загружено. Я искал способ узнать, не удалось ли загрузить изображение. и вы дали мне идею тайм-аута при его загрузке. +1 за это
дуб
1
@oak тайм-аут должен быть только запасным вариантом. большинство браузеров должны запускать errorсобытие, которое обрабатывается .error(function(e) { defer.rejectWith($img); }). Если вы посмотрите на jsFiddle, вы увидите, что http://www.google.com/abc.png not foundтекст отображается сразу в панели результатов (не дожидаясь тайм-аута, потому что произошло событие ошибки)
roberkules
обратите внимание, что для того, чтобы jquery поднял .error, должны быть две вещи: 1. дескриптор, но они должны быть установлены до возникновения ошибки. 2. .error обрабатывает только протокол http, а не file: //, поэтому вы не получите там ошибки ..
oak
в моем примере кода обработчик успеха / ошибки устанавливается перед назначением атрибута src именно по этой причине. об file://ограничении - мне никогда не нужно было связывать изображения с использованием файлового протокола, и я не думаю, что в этом особая необходимость.
roberkules
1

Эта функция проверяет, загружено ли изображение, исходя из измеримых размеров. Этот метод полезен, если ваш скрипт выполняется после того, как некоторые изображения уже были загружены.

imageLoaded = function(node) {
    var w = 'undefined' != typeof node.clientWidth ? node.clientWidth : node.offsetWidth;
    var h = 'undefined' != typeof node.clientHeight ? node.clientHeight : node.offsetHeight;
    return w+h > 0 ? true : false;
};
Ральф Риточ
источник
Я обнаружил, что для этого решения может потребоваться, чтобы на изображении не было полей или отступов, а также предоставлялся альтернативный текст пустой строки. Однако в большинстве случаев это работает так, как задумано. Это не сработает, только если размер выгруженного изображения больше нуля.
Ральф Риточ
1

Я обнаружил, что это сработало для меня

document.querySelector("img").addEventListener("load", function() { alert('onload!'); });

Кредит полностью принадлежит Фрэнку Швитерману, который прокомментировал принятый ответ. Мне пришлось положить это сюда, это слишком ценно ...

Арман
источник
0

Мы разработали страницу, на которую загружалось несколько изображений, а другие функции выполнялись только после загрузки изображения. Это был загруженный сайт, который генерировал много трафика. Похоже, что следующий простой скрипт работал практически во всех браузерах:

$(elem).onload = function() {
    doSomething();
}

НО ЭТО ПОТЕНЦИАЛЬНАЯ ПРОБЛЕМА ДЛЯ IE9!

ЕДИНСТВЕННЫЙ браузер, о котором мы сообщили о проблемах, - это IE9. Мы не удивлены? Кажется, что лучший способ решить эту проблему - не назначать src изображению до тех пор, пока ПОСЛЕ определения функции onload, например:

$(elem).onload = function() {
    doSomething();
}
$(elem).attr('src','theimage.png');

Кажется, что IE 9 иногда не выдает onload событие по какой-либо причине. Другие решения на этой странице (например, решение Эвана Кэрролла) по-прежнему не работали. Логически это проверяет, было ли состояние загрузки уже успешным, и запускает функцию, а если нет, то устанавливает обработчик загрузки, но даже когда вы это делаете, мы продемонстрировали при тестировании, что изображение может загружаться между этими двумя строками js, тем самым отображается не загруженным в первую строку, а затем загружается до того, как будет установлен обработчик загрузки.

Мы обнаружили, что лучший способ получить желаемое - не определять src изображения до тех пор, пока вы не установите onloadтриггер события.

Мы совсем недавно перестали поддерживать IE8, поэтому я не могу говорить о версиях до IE9, иначе из всех других браузеров, которые использовались на сайте - IE10 и 11, а также Firefox, Chrome, Opera, Safari и т. люди использовали мобильный браузер - установка srcперед назначением onloadобработчика даже не была проблемой.

Осьминог
источник
0

Могу я вообще предложить решение на чистом CSS?

Просто имейте Div, в котором вы хотите показать изображение. Установите изображение как фон. Тогда имейте собственность background-size: coverили в background-size: containзависимости от того, как вы этого хотите.

coverбудет обрезать изображение до тех пор, пока меньшие стороны не покроют коробку. containсохранит все изображение внутри div, оставив вам пробелы по бокам.

Посмотрите фрагмент ниже.

div {
  height: 300px;
  width: 300px;
  border: 3px dashed grey;
  background-position: center;
  background-repeat: no-repeat;
}

.cover-image {
  background-size: cover;
}

.contain-image {
  background-size: contain;
}
<div class="cover-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>
<br/>
<div class="contain-image" style="background-image:url(https://assets1.ignimgs.com/2019/04/25/avengers-endgame-1280y-1556226255823_1280w.jpg)">
</div>

Арунвитасмайл
источник
0

Я считаю, что мне лучше всего подходит это простое решение:

        function setEqualHeight(a, b) {
            if (!$(a).height()) {
                return window.setTimeout(function(){ setEqualHeight(a, b); }, 1000);
            }
            $(b).height($(a).height());
        }

        $(document).ready(function() {
            setEqualHeight('#image', '#description');
            $(window).resize(function(){setEqualHeight('#image', '#description')});
        });
    </script>
Мартин Фрэнсис
источник