substr() с отрицательным значением не работает в IE

РЕДАКТИРОВАТЬ: я изменил заголовок, потому что проблема не имела ничего общего с запуском IE image.load() - мой substr() не работал (см. принятый ответ).


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

Это не проблема в моем коде, поскольку именно это я и сделал.

Обратите внимание, что этот сценарий работает во всех других браузерах, но IE 8 и более ранние версии не запускают встроенную функцию загрузки.

var i = lastSlideIndex || 1;

while(imagesQueued < MAX_IMAGES){
    var preloadImage = new Image();
    preloadImage.arrayIndex = i;
    preloadImage.onload = function(eventObj){
        debug('Image ' + this.src + ' loaded.');
        slideshowImages[this.arrayIndex] = this;
        if (this.arrayIndex > lastImageIndex) lastImageIndex = this.arrayIndex;
        triggerSlideshow(this.arrayIndex);
    }

    // add leading zeros
    var leadingZeros = "0000000".substr(-(String(MAX_IMAGES).length));
    imageNumber = leadingZeros.substr(String(i).length) + i;

    debug('preloading Image #' + imageNumber);
    preloadImage.src = fullImagePrefix + imageNumber + "." + IMAGES_TLA;

    if (++i > MAX_IMAGES) i = 1;
    imagesQueued++;
}

Любые другие предложения будут глубоко оценены!


person Tom Auger    schedule 02.08.2011    source источник


Ответы (3)


Обновление: как отметили комментаторы (и пока я не могу им доказать обратное), я удалил первое предложение.

Еще пара вещей, на которые стоит обратить внимание:

Событие onload не сработает, если изображение загружается из кеша. Попробуйте очистить кеш и повторить попытку.

Другая проблема заключается в том, что IE не любит отрицательные значения в substr. Вместо этого используйте slice:

"0000000".slice(-(String(MAX_IMAGES).length));
person Mrchief    schedule 02.08.2011
comment
Нет, это просто неправда. IE определенно поддерживает события загрузки для экземпляров объекта изображения. Если бы это было не так, в конце концов, ваше предложение тоже не сработало бы. Вот jsfiddle. - person Pointy; 03.08.2011
comment
Я согласен с Пойнти. Это неправда. IE (все версии) поддерживает обработчик загрузки изображений. Другой JSFiddle, подтверждающий это: jsfiddle.net/jfriend00/D59SD. - person jfriend00; 03.08.2011
comment
Опять же, вы ошибаетесь. onload срабатывает, когда изображение поступает из кеша, если вы установили обработчик загрузки перед установкой свойства .src. - person jfriend00; 03.08.2011
comment
@ jfriend00 теперь об этом, я не уверен. Смотрите мой ответ. - person Pointy; 03.08.2011
comment
@Pointy, не уверен в чем? Я постоянно использую обработчики загрузки в IE в версиях от IE6 до IE9, и они абсолютно работают, если вы делаете их правильно, независимо от того, кэшируется изображение или нет. - person jfriend00; 03.08.2011
comment
@ jfriend00 Я сделаю вариант скрипки - IE (может быть, только IE8) не вызывает обработчик загрузки, когда src установлен в том же цикле событий, что и обработчик, но он вызовет его, если он либо минует кеш, либо если src установлен в отдельном цикле событий. - person Pointy; 03.08.2011
comment
@Mrchief: спасибо за подсказку substr. Собственно, это и было причиной проблемы. Замена на slice() сделала свое дело, и если бы я обращал внимание только на свою отладку, я бы понял, что она пытается загрузить несуществующие изображения! Отличный улов, чувак. Спасибо - person Tom Auger; 04.08.2011
comment
@Tom: Рад, что это помогло! Недавно была проблема с кем-то еще. ОН думал, что добавление class не работает в IE, но это было связано с той же проблемой, что его код не вызывался. - person Mrchief; 04.08.2011
comment
IE не любит отрицательные значения в substr. Вместо этого используйте срез: вы только что решили для меня это. Спасибо :) +1 - person ConorLuddy; 08.10.2015

Теперь, когда этот вопрос переименован в substr() в IE 8, вот быстрое решение проблемы с отрицательным индексом. Вставьте следующий код из Сеть разработчиков Mozilla:

// only run when the substr() function is broken
if ('ab'.substr(-1) != 'b') {
  /**
   *  Get the substring of a string
   *  @param  {integer}  start   where to start the substring
   *  @param  {integer}  length  how many characters to return
   *  @return {string}
   */
  String.prototype.substr = function(substr) {
    return function(start, length) {
      // call the original method
      return substr.call(this,
        // did we get a negative start, calculate how much it is from the beginning of the string
        // adjust the start parameter for negative value
        start < 0 ? this.length + start : start,
        length);
    }
  }(String.prototype.substr);
};

Этот код обнаруживает неработающую реализацию substr и заменяет ее соответствующей.

person josh    schedule 13.02.2013
comment
Безнравственный. Спасибо, Джош, что поднял эту старую тему и опубликовал что-то действительно полезное! - person Tom Auger; 14.02.2013
comment
Другой вариант — просто заменить substr на slice. - person Bart; 28.02.2013
comment
Будет ли работать, если this.length + start < 0? Это приведет к вызову substr с отрицательным аргументом start, что, по-видимому, даст неверный результат. - person fdermishin; 22.11.2015
comment
@ user502144 похоже, что MDN впоследствии обновил свой полифилл, чтобы устранить эту ошибку ... только что обновил мой ответ, чтобы получить последний код. - person josh; 23.11.2015
comment
@josh Но что будет, если this.length === 5 и start === -7? Есть только проверка start < 0, но она не проверяется, если this.length + start < 0 - person fdermishin; 24.11.2015
comment
@user502144 user502144 вы говорите мне :-) ie 8 не установлен прямо сейчас. что должно произойти, если оно начинается с 0 -- оно должно быть эквивалентно .substr(0, length). Не уверен, что ie 8 делает на практике... если это что-то другое, мы должны сообщить ребятам из MDN... - person josh; 24.11.2015
comment
спасибо, чувак, ты помог мне исправить ошибку проекта gov.uk; Я обменял услугу на небольшое редактирование кода (jslint пометил пару отсутствующих точек с запятой) - person E Ciotti; 03.06.2016

Два способа справиться с этим:

  1. Добавьте параметр nonce к своим URL-адресам изображений.

    var nonce = new Date().getTime();
    
    // ...
    
    preloadImage.src = fullImagePrefix + imageNumber + "." + IMAGES_TLA + ('?_=' + nonce++);
    
  2. Установите свойство "src" в другом цикле событий.

    setTimeout(function(img, src) {
      img.src = src;
    }(preloadImage, fullImagePrefix + imageNumber + "." + IMAGES_TLA), 1);
    

Используя параметр nonce каждый раз, когда вы извлекаете изображение, вы обходите кеш. Это, вероятно, не такая уж хорошая идея, поэтому второй вариант позволяет обойти проблему, убедившись, что свойство «src» установлено в отдельном цикле событий. Тогда «нагрузка» сработает.

Вот пример. Код использует одноразовые номера для некоторых изображений, но устанавливает «src» для всех из них в обработчике тайм-аута. Как видите, все они загружаются (краснеют).

Я не знаю, почему IE не запускает обработчик «загрузки», когда изображение находится в кеше, но это происходит, когда «src» устанавливается в цикле событий, отличном от того, где объект изображения (в противном случае) инициализируется.

редактировать Вот та же самая скрипка, но измененная, чтобы пропустить тайм-аут. Вы должны заметить, что в IE8 изображения с четными номерами часто не вызываются обработчиками «загрузки».

person Pointy    schedule 02.08.2011
comment
Я действительно не уверен, что вы видите, когда, по вашему мнению, IE8 не может вызвать обработчик загрузки без использования тайм-аута и без одноразового номера. В вашем jsFiddle без одноразового номера и без тайм-аута я всегда вижу обработчик загрузки, вызываемый в моей копии IE8. У меня есть слайд-шоу javascript, которое зависит от обработчика загрузки (оно не будет отображать изображение, если обработчик загрузки не срабатывает), и оно используется на сотнях веб-сайтов (включая мой собственный), и у меня никогда не было отчета проблемы с загрузкой изображений в IE8. Что-то еще должно происходить, когда вы видите, что это терпит неудачу. - person jfriend00; 03.08.2011
comment
К вашему сведению, было бы немного проще добраться до основной проблемы, если бы вы не использовали jQuery для части обработчика загрузки ваших примеров. Поскольку мы говорим о голом обработчике загрузки, было бы неплохо, если бы речь шла только об этом. - person jfriend00; 03.08.2011
comment
Вот моя чисто JS-скрипка: jsfiddle.net/jfriend00/YU56G, которая загружает 6 изображений с обработчиками загрузки и без одноразовых номеров и без использования setTimeout. Я не могу заставить его пропустить обработчик загрузки в IE8. @Pointy - вы видите сбои в этом в IE8? - person jfriend00; 03.08.2011
comment
Спасибо за предложения @Pointy. Просто обратите внимание, что задержка 1 не рекомендуется Mozilla, поэтому следите за этим (минимальная задержка, DOM_MIN_TIMEOUT_VALUE, составляет 4 мс (хранится в настройке в Firefox: dom.min_timeout_value), с DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 мс.) , вы хотели использовать замыкание в качестве первого аргумента в setTimeout()? - person Tom Auger; 04.08.2011