jQuery / JavaScript para reemplazar imágenes rotas

547

Tengo una página web que incluye un montón de imágenes. A veces la imagen no está disponible, por lo que se muestra una imagen rota en el navegador del cliente.

¿Cómo uso jQuery para obtener el conjunto de imágenes, filtrarlo a imágenes rotas y luego reemplazar el src?


Pensé que sería más fácil hacer esto con jQuery, pero resultó mucho más fácil usar una solución de JavaScript pura, es decir, la que proporcionó Prestaul.

Dan Lord
fuente

Respuestas:

760

Maneje el onErrorevento para que la imagen reasigne su fuente usando JavaScript:

function imgError(image) {
    image.onerror = "";
    image.src = "/images/noimage.gif";
    return true;
}
<img src="image.png" onerror="imgError(this);"/>

O sin una función de JavaScript:

<img src="image.png" onError="this.onerror=null;this.src='/images/noimage.gif';" />

La siguiente tabla de compatibilidad enumera los navegadores que admiten la función de error:

http://www.quirksmode.org/dom/events/error.html

Prestaul
fuente
132
Probablemente desee eliminar un error antes de configurar src. De lo contrario, si también falta noimage.gif, puede terminar con un "desbordamiento de pila".
pcorcoran
45
Pensé y esperaba que estábamos alejando de atributos en línea de los eventos de JavaScript ...
redsquare
24
redsquare ... son perfectamente comprensibles cuando eres juicioso sobre mantenerlos bajo control e incluso pueden ser útiles cuando quieres que tu marcado refleje lo que realmente va a suceder.
Nicole
2
Buen punto NickC, sobre el marcado que muestra lo que está sucediendo, simplemente me estremezco cuando lo miro: P
SSH This
38
@redsquare, estoy completamente de acuerdo, pero este es un caso único. Inline es el único lugar donde puede adjuntar al oyente con absoluta certeza de que el evento no se activará antes de que se adjunte el oyente. Si lo hace al final del documento o espera a que se cargue el DOM, puede perder el evento de error en algunas imágenes. No sirve de nada adjuntar un controlador de errores después de que se haya activado el evento.
Prestaul
201

Yo uso el errorcontrolador incorporado :

$("img").error(function () {
    $(this).unbind("error").attr("src", "broken.gif");
});

Editar: El error()método está en desuso en jquery 1.8 y superior. En su lugar, debe usar .on("error")en su lugar:

$("img").on("error", function () {
    $(this).attr("src", "broken.gif");
});
travis
fuente
112
Si usa esta técnica, puede usar el método "one" para evitar la necesidad de desvincular el evento: $ ('img'). One ('error', function () {this.src = 'broken.gif';}) ;
Prestaul
77
+1 ¿Pero tienes que desatarlo? Funciona maravillosamente en imágenes dinámicas (por ejemplo, las que pueden cambiar al pasar el mouse) si no lo desvincula.
Aximili
15
Creo que si no lo desvincula, y la referencia src broken.gif nunca se carga por alguna razón, podrían ocurrir cosas malas en el navegador.
travis
44
@travis, ¿en qué punto harías esta llamada? ¿Hay alguna forma de usar este método y asegurarse de que su controlador se conecte ANTES de que se active el evento de error?
Prestaul
55
Aquí hay un ejemplo de un caso en el que el error se dispara antes de que se adjunte el controlador de eventos: jsfiddle.net/zmpGz/1 Estoy realmente curioso si hay una forma segura de conectar este controlador ...
Prestaul
120

En caso de que alguien como yo intente adjuntar el errorevento a una imgetiqueta HTML dinámica , me gustaría señalar que hay un problema:

Aparentemente, imglos eventos de error no aparecen en la mayoría de los navegadores, al contrario de lo que dice el estándar .

Entonces, algo como lo siguiente no funcionará :

$(document).on('error', 'img', function () { ... })

Espero que esto sea útil para alguien más. Desearía haber visto esto aquí en este hilo. Pero no lo hice. Entonces, lo estoy agregando

mouad
fuente
no funcionó Cargo una imagen muerta dinámicamente y la agrego al dom, y no se dispara ningún evento de 'error'.
vsync
59

Aquí hay una solución independiente:

$(window).load(function() {
  $('img').each(function() {
    if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
      // image was broken, replace with your new image
      this.src = 'http://www.tranism.com/weblog/images/broken_ipod.gif';
    }
  });
});
Devon
fuente
55
Casi correcto ... una imagen correcta en IE todavía regresará (typeof (this.naturalWidth) == "undefined") == true; - Cambié la instrucción if a (! This.complete || (! $. Browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)))
Sugendran
3
esto gobierna porque puede detectar una imagen rota después de cargar una página. Necesitaba esto.
norteamericano
@Sugendran $.browserha sido desaprobado y eliminado ahora, use la detección de funciones en su lugar (se vuelve más complicado en este caso).
lee penkman
3
Independiente pero requiere jQuery?
Charlie
Esto funciona muy bien, pero cuando el src de la imagen regresa 204 No Content, sería una imagen rota.
Carson Reinke
35

Creo que esto es lo que buscas : jQuery.Preload

Aquí está el código de ejemplo de la demostración, especifica la carga y las imágenes no encontradas y ya está todo listo:

$('#images img').preload({
    placeholder:'placeholder.jpg',
    notFound:'notfound.jpg'
});
Nick Craver
fuente
1
jQuery.Preload es un complemento que se requiere para usar el código de ejemplo.
Dan Lord
17
Sí ... esto está vinculado en la respuesta en la primera línea.
Nick Craver
Gracias Nick, esta parece ser una buena solución para el problema de más de 8 años de que Firefox no muestra un reemplazo de imagen para imágenes rotas. Además, puedes marcar la pequeña cosa ... gracias de nuevo.
klewis
25
$(window).bind('load', function() {
$('img').each(function() {
    if((typeof this.naturalWidth != "undefined" &&
        this.naturalWidth == 0 ) 
        || this.readyState == 'uninitialized' ) {
        $(this).attr('src', 'missing.jpg');
    }
}); })

Fuente: http://www.developria.com/2009/03/jquery-quickie---broken-images.html

Mohamad
fuente
1
Se corrigió el problema de que onerror no se disparara en firefox :) Gracias.
ram
19

Si bien el OP estaba buscando reemplazar el SRC, estoy seguro de que muchas personas que respondieron a esta pregunta solo desean ocultar la imagen rota, en cuyo caso esta solución simple funcionó muy bien para mí.

Usando JavaScript en línea:

<img src="img.jpg" onerror="this.style.display='none';" />

Usando JavaScript externo:

var images = document.querySelectorAll('img');

for (var i = 0; i < images.length; i++) {
  images[i].onerror = function() {
    this.style.display='none';
  }
}
<img src='img.jpg' />

Usando JavaScript externo moderno:

document.querySelectorAll('img').forEach((img) => {
  img.onerror = function() {
    this.style.display = 'none';
  }
});
<img src='img.jpg' />

Consulte el soporte del navegador para NoteList.forEach y las funciones de flecha .

Nathan Arthur
fuente
1
He encontrado esto como la solución más confiable para un problema muy similar. Prefiero no mostrar ninguna imagen que mostrar una alternativa de imagen rota. Gracias.
pc_
Obviamente, esto no funciona donde la Política de seguridad de contenido no permite secuencias de comandos en línea.
Isherwood
@isherwood Buen punto. He agregado una solución que funciona a través de un archivo JS externo.
Nathan Arthur el
11

Aquí hay una manera rápida y sucia de reemplazar todas las imágenes rotas, y no hay necesidad de cambiar el código HTML;)

ejemplo de codepen

    $("img").each(function(){
        var img = $(this);
        var image = new Image();
        image.src = $(img).attr("src");
        var no_image = "https://dummyimage.com/100x100/7080b5/000000&text=No+image";
        if (image.naturalWidth == 0 || image.readyState == 'uninitialized'){
            $(img).unbind("error").attr("src", no_image).css({
                height: $(img).css("height"),
                width: $(img).css("width"),
            });
        }
  });
Ashfaq Ahmed
fuente
9

No pude encontrar un script que se adaptara a mis necesidades, por lo que hice una función recursiva para verificar si hay imágenes rotas e intentar volver a cargarlas cada cuatro segundos hasta que se arreglen.

Lo limité a 10 intentos como si no estuviera cargado para entonces, la imagen podría no estar presente en el servidor y la función entraría en un bucle infinito. Aunque todavía estoy probando. Siéntase libre de modificarlo :)

var retries = 0;
$.imgReload = function() {
    var loaded = 1;

    $("img").each(function() {
        if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {

            var src = $(this).attr("src");
            var date = new Date();
            $(this).attr("src", src + "?v=" + date.getTime()); //slightly change url to prevent loading from cache
            loaded =0;
        }
    });

    retries +=1;
    if (retries < 10) { // If after 10 retries error images are not fixed maybe because they
                        // are not present on server, the recursion will break the loop
        if (loaded == 0) {
            setTimeout('$.imgReload()',4000); // I think 4 seconds is enough to load a small image (<50k) from a slow server
        }
        // All images have been loaded
        else {
            // alert("images loaded");
        }
    }
    // If error images cannot be loaded  after 10 retries
    else {
        // alert("recursion exceeded");
    }
}

jQuery(document).ready(function() {
    setTimeout('$.imgReload()',5000);
});
Sombra
fuente
Buena idea al tratar de recargar imágenes. Esto es útil para las imágenes que se cargan / generan automáticamente y el servidor podría no haberse completado / completado cuando se carga la página. Ese es un formato original. Un poco demasiado inconsistente y no de mi gusto sin embargo ...
Joakim
8

Esta es una técnica horrible, pero está prácticamente garantizada:

<img ...  onerror="this.parentNode.removeChild(this);">
Phil LaNasa
fuente
6

Puede usar la propia búsqueda de GitHub para esto:

Frontend: https://github.com/github/fetch
o para Backend, una versión de Node.js: https://github.com/bitinn/node-fetch

fetch(url)
  .then(function(res) {
    if (res.status == '200') {
      return image;
    } else {
      return placeholder;
    }
  }

Editar: este método reemplazará a XHR y supuestamente ya ha estado en Chrome. Para cualquiera que lea esto en el futuro, es posible que no necesite la biblioteca mencionada incluida.

BarryMode
fuente
6

Este es JavaScript, debe ser compatible con varios navegadores y se entrega sin el marcado feo onerror="":

var sPathToDefaultImg = 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
    validateImage = function( domImg ) {
        oImg = new Image();
        oImg.onerror = function() {
            domImg.src = sPathToDefaultImg;
        };
        oImg.src = domImg.src;
    },
    aImg = document.getElementsByTagName( 'IMG' ),
    i = aImg.length;

while ( i-- ) {
    validateImage( aImg[i] );
}

CODEPEN:

Axel
fuente
Votación a favorwhile (i--)
s3c
4

Mejor llamada usando

jQuery(window).load(function(){
    $.imgReload();
});

Porque el uso document.readyno necesariamente implica que las imágenes se carguen, solo el HTML. Por lo tanto, no hay necesidad de una llamada retrasada.

Sombra
fuente
4

Variante CoffeeScript :

Lo solucioné para solucionar un problema con Turbolinks que hace que el método .error () aparezca en Firefox a veces a pesar de que la imagen está realmente allí.

$("img").error ->
  e = $(@).get 0
  $(@).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
Kevin
fuente
4

Al usar la respuesta de Prestaul , agregué algunas comprobaciones y prefiero usar la forma jQuery.

<img src="image1.png" onerror="imgError(this,1);"/>
<img src="image2.png" onerror="imgError(this,2);"/>

function imgError(image, type) {
    if (typeof jQuery !== 'undefined') {
       var imgWidth=$(image).attr("width");
       var imgHeight=$(image).attr("height");

        // Type 1 puts a placeholder image
        // Type 2 hides img tag
        if (type == 1) {
            if (typeof imgWidth !== 'undefined' && typeof imgHeight !== 'undefined') {
                $(image).attr("src", "http://lorempixel.com/" + imgWidth + "/" + imgHeight + "/");
            } else {
               $(image).attr("src", "http://lorempixel.com/200/200/");
            }
        } else if (type == 2) {
            $(image).hide();
        }
    }
    return true;
}
trante
fuente
4

Si ha insertado su imgcon innerHTML, como:, $("div").innerHTML = <img src="wrong-uri">puede cargar otra imagen si no funciona, por ejemplo, esto:

<script>
    function imgError(img) {
        img.error="";
        img.src="valid-uri";
    }
</script>

<img src="wrong-uri" onerror="javascript:imgError(this)">

¿Por qué es javascript: _necesario? Debido a que las secuencias de comandos inyectadas en el DOM mediante etiquetas de secuencia de comandos innerHTMLno se ejecutan en el momento en que se inyectan, debe ser explícito.

horro
fuente
4

Encontré esta publicación mientras miraba esta otra publicación SO . A continuación hay una copia de la respuesta que di allí.

Sé que este es un hilo viejo, pero React se ha vuelto popular y, tal vez, alguien que usa React vendrá aquí buscando una respuesta al mismo problema.

Entonces, si está usando React, puede hacer algo como lo siguiente, que fue una respuesta original proporcionada por Ben Alpert del equipo React aquí

getInitialState: function(event) {
    return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
    this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
    return (
        <img onError={this.handleError} src={src} />;
    );
}
Jordan Bonitatis
fuente
4

Creé un violín para reemplazar la imagen rota usando el evento "onerror". Esto puede ayudarte.

    //the placeholder image url
    var defaultUrl = "url('https://sadasd/image02.png')";

    $('div').each(function(index, item) {
      var currentUrl = $(item).css("background-image").replace(/^url\(['"](.+)['"]\)/, '$1');
      $('<img>', {
        src: currentUrl
      }).on("error", function(e) {
        $this = $(this);
        $this.css({
          "background-image": defaultUrl
        })
        e.target.remove()
      }.bind(this))
    })
Ninjaneer
fuente
4

Aquí hay un ejemplo usando el objeto de imagen HTML5 envuelto por JQuery. Llame a la función de carga para la URL de la imagen principal y si esa carga causa un error, reemplace el atributo src de la imagen con una URL de respaldo.

function loadImageUseBackupUrlOnError(imgId, primaryUrl, backupUrl) {
    var $img = $('#' + imgId);
    $(new Image()).load().error(function() {
        $img.attr('src', backupUrl);
    }).attr('src', primaryUrl)
}

<img id="myImage" src="primary-image-url"/>
<script>
    loadImageUseBackupUrlOnError('myImage','primary-image-url','backup-image-url');
</script>
Kurt Hartmann
fuente
4

JS puro. Mi tarea era: si la imagen 'bl-once.png' está vacía -> inserte la primera imagen (que no tiene estado 404) de la lista de matriz (en el directorio actual):

<img src="http://localhost:63342/GetImage/bl-once.png" width="200" onerror="replaceEmptyImage.insertImg(this)">

Tal vez necesita ser mejorado, pero:

var srcToInsertArr = ['empty1.png', 'empty2.png', 'needed.png', 'notActual.png']; // try to insert one by one img from this array
    var path;
    var imgNotFounded = true; // to mark when success

    var replaceEmptyImage = {
        insertImg: function (elem) {

            if (srcToInsertArr.length == 0) { // if there are no more src to try return
                return "no-image.png";
            }
            if(!/undefined/.test(elem.src)) { // remember path
                path = elem.src.split("/").slice(0, -1).join("/"); // "http://localhost:63342/GetImage"
            }
            var url = path + "/" + srcToInsertArr[0];

            srcToInsertArr.splice(0, 1); // tried 1 src

            
                if(imgNotFounded){ // while not success
                    replaceEmptyImage.getImg(url, path, elem); // CALL GET IMAGE
                }
            

        },
        getImg: function (src, path, elem) { // GET IMAGE

            if (src && path && elem) { // src = "http://localhost:63342/GetImage/needed.png"
                
                var pathArr = src.split("/"); // ["http:", "", "localhost:63342", "GetImage", "needed.png"]
                var name = pathArr[pathArr.length - 1]; // "needed.png"

                xhr = new XMLHttpRequest();
                xhr.open('GET', src, true);
                xhr.send();

                xhr.onreadystatechange = function () {

                    if (xhr.status == 200) {
                        elem.src = src; // insert correct src
                        imgNotFounded = false; // mark success
                    }
                    else {
                        console.log(name + " doesn't exist!");
                        elem.onerror();
                    }

                }
            }
        }

    };

Por lo tanto, insertará el correcto 'needed.png' en mi src o 'no-image.png' desde el directorio actual.

Дмитрий Дорогонов
fuente
Lo he usado en el mismo sitio. Esto es solo idea. Mi código real se ha mejorado ...
Дмитрий Дорогонов
3

No estoy seguro de si hay una mejor manera, pero puedo pensar en un truco para obtenerlo: podría publicar Ajax en la URL de img y analizar la respuesta para ver si la imagen realmente regresó. Si volvió como un 404 o algo así, cambie el img. Aunque espero que esto sea bastante lento.

Chii
fuente
3

Resolví mi problema con estas dos funciones simples:

function imgExists(imgPath) {
    var http = jQuery.ajax({
                   type:"HEAD",
                   url: imgPath,
                   async: false
               });
    return http.status != 404;
}

function handleImageError() {
    var imgPath;

    $('img').each(function() {
        imgPath = $(this).attr('src');
        if (!imgExists(imgPath)) {
            $(this).attr('src', 'images/noimage.jpg');
        }
    });
}
Luis Gustavo Beligante
fuente
3

jQuery 1.8

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .error(function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

jQuery 3

// If missing.png is missing, it is replaced by replacement.png
$( "img" )
  .on("error", function() {
    $( this ).attr( "src", "replacement.png" );
  })
  .attr( "src", "missing.png" );

referencia

Nabi KAZ
fuente
3

Creo que tengo una forma más elegante con la delegación de eventos y captura de eventos en window's errorincluso cuando la imagen de copia de seguridad no se pueden cargar.

img {
  width: 100px;
  height: 100px;
}
<script>
  window.addEventListener('error', windowErrorCb, {
    capture: true
  }, true)

  function windowErrorCb(event) {
    let target = event.target
    let isImg = target.tagName.toLowerCase() === 'img'
    if (isImg) {
      imgErrorCb()
      return
    }

    function imgErrorCb() {
      let isImgErrorHandled = target.hasAttribute('data-src-error')
      if (!isImgErrorHandled) {
        target.setAttribute('data-src-error', 'handled')
        target.src = 'backup.png'
      } else {
        //anything you want to do
        console.log(target.alt, 'both origin and backup image fail to load!');
      }
    }
  }
</script>
<img id="img" src="error1.png" alt="error1">
<img id="img" src="error2.png" alt="error2">
<img id="img" src="https://i.stack.imgur.com/ZXCE2.jpg" alt="avatar">

La cuestión es :

  1. Ponga el código en heady ejecutado como el primer script en línea. Entonces, escuchará los errores que suceden después del guión.

  2. Use la captura de eventos para detectar los errores, especialmente para aquellos eventos que no burbujean.

  3. Utilice la delegación de eventos que evita eventos vinculantes en cada imagen.

  4. Otorgue imgun atributo al elemento de error después de otorgarles un backup.pngpara evitar la desaparición del backup.pngbucle infinito y posterior como se muestra a continuación:

img error-> backup.png-> error-> backup.png-> error -> ,,,,,

xianshenglu
fuente
Esta es, con mucho, la mejor respuesta. Lo único que sugeriría es usar, let isImgErrorHandled = target.src === 'backup.png';ya que simplifica un poco.
Sabo
@Sabo, hay un pequeño problema porque la ruta src puede no ser igual a la ruta que asigné. Por ejemplo, target.src='backup.png'pero la próxima vez console.log(target.src)puede no serbackup.png
xianshenglu
2
;(window.jQuery || window.Zepto).fn.fallback = function (fallback) {
    return this.one('error', function () {
        var self = this;
        this.src = (fallback || 'http://lorempixel.com/$width/$height')
        .replace(/\$(\w+)/g, function (m, t) { return self[t] || ''; });
    });
};

Puede pasar una ruta de marcador de posición y acceder a ella todas las propiedades del objeto de imagen fallido a través de $*:

$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');

http://jsfiddle.net/ARTsinn/Cu4Zn/

yckart
fuente
2

Esto me ha estado frustrando durante años. Mi corrección de CSS establece una imagen de fondo en el img. Cuando una imagen dinámica srcno se carga en primer plano, se visualiza un marcador de posición en el imgbg del 's. Esto funciona si las imágenes tienen un tamaño predeterminado (por ejemplo height, min-height, widthy / o min-width).

Verá el icono de imagen rota, pero es una mejora. Probado hasta IE9 con éxito. iOS Safari y Chrome ni siquiera muestran un ícono roto.

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
}

Agregue una pequeña animación para dar srctiempo a cargar sin un parpadeo de fondo. Chrome se desvanece en el fondo sin problemas, pero Safari de escritorio no.

.dynamicContainer img {
  background: url('/images/placeholder.png');
  background-size: contain;
  -webkit-animation: fadein 1s;
  animation: fadein 1s;                     
}

@-webkit-keyframes fadein {
  0%   { opacity: 0.0; }
  50%  { opacity: 0.5; }
  100% { opacity: 1.0; }
}

@keyframes fadein {
  0%   { opacity: 0.0; }
  50%  { opacity: 0.5; }
  100% { opacity: 1.0; }
}
Dylan Valade
fuente
2

Si la imagen no se puede cargar (por ejemplo, porque no está presente en la URL suministrada), la URL de la imagen se cambiará a predeterminada,

Para más información sobre .error ()

$('img').on('error', function (e) {
  $(this).attr('src', 'broken.png');
});

Casper
fuente
1

Tengo el mismo problema. Este código funciona bien en mi caso.

// Replace broken images by a default img
$('img').each(function(){
    if($(this).attr('src') === ''){
      this.src = '/default_feature_image.png';
    }
});
Dale Nguyen
fuente
1

A veces, utilizar el errorevento no es factible, por ejemplo, porque está intentando hacer algo en una página que ya está cargada, como cuando ejecuta código a través de la consola, un marcador o un script cargado de forma asincrónica. En ese caso, verificar eso img.naturalWidthy img.naturalHeightson 0 parece ser el truco.

Por ejemplo, aquí hay un fragmento para volver a cargar todas las imágenes rotas desde la consola:

$$("img").forEach(img => {
  if (!img.naturalWidth && !img.naturalHeight) {
    img.src = img.src;
  }
}
Lea Verou
fuente
0

Encontré que esto funciona mejor, si alguna imagen no se carga la primera vez, se elimina completamente del DOM. La ejecución console.clear()mantiene limpia la ventana de la consola, ya que los errores 404 no se pueden omitir con los bloques try / catch.

$('img').one('error', function(err) {
    // console.log(JSON.stringify(err, null, 4))
    $(this).remove()
    console.clear()
})
decodificador7283
fuente