ตรวจจับเมื่อภาพไม่สามารถโหลดใน Javascript


105

มีวิธีตรวจสอบว่าเส้นทางรูปภาพนำไปสู่รูปภาพจริงหรือไม่เช่นตรวจจับเมื่อรูปภาพไม่สามารถโหลดใน Javascript

สำหรับเว็บแอปฉันกำลังแยกวิเคราะห์ไฟล์ xml และสร้างภาพ HTML แบบไดนามิกจากรายการเส้นทางรูปภาพ เส้นทางรูปภาพบางส่วนอาจไม่มีอยู่บนเซิร์ฟเวอร์อีกต่อไปดังนั้นฉันจึงต้องการล้มเหลวอย่างสง่างามโดยการตรวจจับว่ารูปภาพใดโหลดไม่สำเร็จและลบองค์ประกอบ HTML img นั้น

หมายเหตุโซลูชัน JQuery จะไม่สามารถใช้ได้ (เจ้านายไม่ต้องการใช้ JQuery ใช่ฉันรู้ว่าฉันไม่ได้เริ่มต้น) ฉันรู้วิธีใน JQuery ในการตรวจจับเมื่อโหลดรูปภาพ แต่ไม่ว่าจะล้มเหลวหรือไม่

รหัสของฉันในการสร้างองค์ประกอบ img แต่ฉันจะตรวจสอบได้อย่างไรว่าเส้นทาง img นำไปสู่การโหลดภาพล้มเหลว

var imgObj = new Image();  // document.createElement("img");
imgObj.src = src;

สิ่งนี้อาจช่วยคุณได้: stackoverflow.com/questions/1977871/… (เป็น jQuery แต่ก็ยังอาจนำคุณไปถูกทาง)
Ben Lee

ลองใช้google.com/…
ajax333221

2
ขำ ๆ @ ajax333221 คำถามนี้เป็นอันดับแรกในผลลัพธ์ของลิงค์ของคุณ :)
SSH

เขียนตัวเลือก JQuery เพื่อค้นหา Boss ใหม่ ... อินเทอร์เน็ตเป็นสถานที่ขนาดใหญ่
JJS

คำตอบ:


116

คุณสามารถลองใช้รหัสต่อไปนี้ ฉันไม่สามารถรับรองความเข้ากันได้ของเบราว์เซอร์ดังนั้นคุณจะต้องทดสอบ

function testImage(URL) {
    var tester=new Image();
    tester.onload=imageFound;
    tester.onerror=imageNotFound;
    tester.src=URL;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

และความเห็นอกเห็นใจของฉันสำหรับเจ้านายที่ทน jQuery!


2
มีความคิดใดบ้างที่จะทราบได้ว่าพบการเปลี่ยนเส้นทางหรือไม่?
quickshiftin

4
สิ่งนี้ใช้ไม่ได้บน webKit (Epiphany, Safari, ... ) ขึ้นอยู่กับเซิร์ฟเวอร์ที่คุณได้รับภาพจาก ตัวอย่างเช่นบางครั้ง imgur จะไม่ส่งรูปภาพที่มีอยู่หรือสถานะ 404 และเหตุการณ์ข้อผิดพลาดจะไม่เกิดขึ้นในกรณีนี้

ปัญหานี้คือข้อความของคุณ "ไม่พบภาพนั้น" รหัสตอบกลับอาจเป็น 500 หรือ 403 หรืออย่างอื่นก็ได้ คุณไม่รู้ว่ามันคือ 404 อันที่จริงมันไม่น่าเป็นไปได้มากนักที่มันจะเป็น 404 เนื่องจากคุณอาจจะไม่ลองโหลดภาพที่ไม่มี
PHP Guru

1
ควรเพิ่มตัวจัดการเหตุการณ์แทนการกำหนดตำแหน่งโดยตรง
cdalxndr

50

คำตอบนั้นดี แต่แนะนำปัญหาหนึ่ง เมื่อใดก็ตามที่คุณมอบหมายonloadหรือonerrorโดยตรงอาจแทนที่การเรียกกลับที่ได้รับมอบหมายก่อนหน้านี้ นั่นคือเหตุผลที่มีวิธีที่ดีที่"ลงทะเบียนผู้ฟังที่ระบุบน EventTarget ก็เรียกว่า" การที่พวกเขาพูดเกี่ยวกับMDN คุณสามารถลงทะเบียนผู้ฟังได้มากเท่าที่คุณต้องการในกิจกรรมเดียวกัน

ให้ฉันเขียนคำตอบใหม่สักหน่อย

function testImage(url) {
    var tester = new Image();
    tester.addEventListener('load', imageFound);
    tester.addEventListener('error', imageNotFound);
    tester.src = url;
}

function imageFound() {
    alert('That image is found and loaded');
}

function imageNotFound() {
    alert('That image was not found.');
}

testImage("http://foo.com/bar.jpg");

เนื่องจากกระบวนการโหลดทรัพยากรภายนอกเป็นแบบอะซิงโครนัสจึงเป็นการดีกว่าที่จะใช้ JavaScript ที่ทันสมัยกับสัญญาดังต่อไปนี้

function testImage(url) {

    // Define the promise
    const imgPromise = new Promise(function imgPromise(resolve, reject) {

        // Create the image
        const imgElement = new Image();

        // When image is loaded, resolve the promise
        imgElement.addEventListener('load', function imgOnLoad() {
            resolve(this);
        });

        // When there's an error during load, reject the promise
        imgElement.addEventListener('error', function imgOnError() {
            reject();
        })

        // Assign URL
        imgElement.src = url;

    });

    return imgPromise;
}

testImage("http://foo.com/bar.jpg").then(

    function fulfilled(img) {
        console.log('That image is found and loaded', img);
    },

    function rejected() {
        console.log('That image was not found');
    }

);

2
บางทีฉันอาจจะขาดบางอย่างไป แต่งาน (tester.onload = .. ) จะแทนที่การโทรกลับได้อย่างไรหากเพิ่งสร้าง "ผู้ทดสอบ" แม้ว่าโค้ดบางส่วนที่คลุมเครือจะเพิ่มเหตุการณ์ให้กับทุกภาพที่สร้างขึ้น แต่ก็ไม่ชัดเจนว่าเราต้องการเก็บเหตุการณ์เหล่านั้นไว้เพื่อตรวจจับว่ามีรูปภาพอยู่หรือไม่
jvilhena

1
คุณถูกต้องที่สุด ในกรณีนี้อาจไม่ใช่ปัญหาเพราะมีโอกาสสูงที่จะไม่สามารถเปลี่ยนได้ อย่างไรก็ตามโดยทั่วไปแล้วการเพิ่มตัวฟังเหตุการณ์จะเป็นการดีกว่าการกำหนดวิธีการโดยตรง
emil.c

1
ฟังก์ชันลูกศรของ @JohnWeisz ไม่ระบุชื่อดังนั้นจึงยากที่จะดีบักใช้อย่างระมัดระวัง
emil.c

10
@GifCo ฉันไม่สบายใจที่จะสนทนากับคุณต่อไป ฉันรู้สึกไม่พอใจกับภาษาของคุณ หากคุณเชื่อว่าคำตอบของฉันไม่ถูกต้องหรือไม่สมบูรณ์โปรดแนะนำการเปลี่ยนแปลงและอธิบายเหตุผลของคุณ หากคุณคิดว่าบางอย่างเป็นการปฏิบัติที่ไม่ดีแนะนำวิธีปฏิบัติที่ดีพร้อมกับคำตอบของคุณเอง
emil.c

2
เหตุใดจึงมีการอภิปรายเกี่ยวกับฟังก์ชันลูกศรที่นี่ มันอยู่นอกหัวข้อ คำตอบนั้นดีโดยเฉพาะอย่างยิ่งเนื่องจากเบราว์เซอร์รุ่นเก่าไม่รองรับฟังก์ชันลูกศร (เช่น Internet Explorer)
PHP Guru

30

นี้:

<img onerror="this.src='/images/image.png'" src="...">

1
แม้ว่านี่จะเป็นแอตทริบิวต์ที่มีประโยชน์ซึ่งสามารถใช้เป็นเทคนิคการเฟลโอเวอร์ของรูปภาพได้ แต่การตอบกลับแบบละเอียดมากขึ้นจะเป็นประโยชน์สำหรับผู้อ่านในการทำความเข้าใจ
sorak

2
สั้นและมีประโยชน์มาก! เพื่อที่จะซ่อนมัน:<img src="your/path/that/might/fail.png" onerror="$(this).hide();"/>
J0ANMM

2
ความคิดเห็นของ @ sorak ทำให้ฉันหัวเราะด้วยความรังเกียจ นี่คือคำตอบที่ถูกต้อง
user3751385

@ J0ANMM อีกวิธีในการซ่อนรูปภาพคือการเพิ่มแอตทริบิวต์ alt ว่าง: <img alt = "" src = "yourpicture.png">
Rick Glimmer

ยอดเยี่ยม! แทนที่จะล้มเหลวในภาพฉันพิมพ์ข้อความนี้ onerror = "this.alt = 'Not Valid Image, Use Link ^'"
ขับเหงื่อ

6
/**
 * Tests image load.
 * @param {String} url
 * @returns {Promise}
 */
function testImageUrl(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();
    image.addEventListener('load', resolve);
    image.addEventListener('error', reject);
    image.src = url;
  });
}

return testImageUrl(imageUrl).then(function imageLoaded(e) {
  return imageUrl;
})
.catch(function imageFailed(e) {
  return defaultImageUrl;
});

4

jQuery + CSS สำหรับ img

ด้วย jQuery สิ่งนี้ใช้ได้กับฉัน:

$('img').error(function() {
    $(this).attr('src', '/no-img.png').addClass('no-img');
});

และฉันสามารถใช้รูปภาพนี้ได้ทุกที่บนเว็บไซต์โดยไม่คำนึงถึงขนาดของรูปภาพด้วยคุณสมบัติ CSS3 ต่อไปนี้:

img.no-img {
    object-fit: cover;
    object-position: 50% 50%;
}

เคล็ดลับ 1: ใช้ภาพสี่เหลี่ยมจัตุรัสอย่างน้อย 800 x 800 พิกเซล

เคล็ดลับ 2: สำหรับใช้กับภาพบุคคลให้ใช้object-position: 20% 50%;

CSS สำหรับ background-img เท่านั้น

สำหรับภาพพื้นหลังที่หายไปฉันได้เพิ่มสิ่งต่อไปนี้ในbackground-imageการประกาศแต่ละครั้ง:

background-image: url('path-to-image.png'), url('no-img.png');

หมายเหตุ: ไม่ทำงานกับภาพโปร่งใส

ฝั่งเซิร์ฟเวอร์ Apache

อีกวิธีหนึ่งคือการตรวจจับภาพที่หายไปด้วย Apache ก่อนที่จะส่งไปที่เบราว์เซอร์และจัดวางใหม่ตามค่าเริ่มต้นของเนื้อหาที่ไม่มี img.png

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} /images/.*\.(gif|jpg|jpeg|png)$
RewriteRule .* /images/no-img.png [L,R=307]

3

ต่อไปนี้เป็นฟังก์ชั่นที่ผมเขียนคำตอบอื่น: จาวาสคริ URL ของภาพที่ตรวจสอบ ผมไม่ทราบว่ามันเป็นสิ่งที่คุณต้องการ แต่จะใช้เทคนิคต่างๆที่คุณจะใช้ซึ่งรวมถึงการขนย้ายวัสดุสำหรับonload, onerror, onabortและหมดเวลาทั่วไป

เนื่องจากการโหลดรูปภาพเป็นแบบอะซิงโครนัสคุณจึงเรียกใช้ฟังก์ชันนี้กับรูปภาพของคุณจากนั้นจะเรียกใช้การโทรกลับของคุณในภายหลังพร้อมกับผลลัพธ์


-19

เช่นเดียวกับด้านล่าง:

var img = new Image(); 
img.src = imgUrl; 

if (!img.complete) {

//has picture
}
else //not{ 

}

3
ไม่ สิ่งนี้อาจทำให้คุณทราบได้ว่ารูปภาพนั้นถูกแคชไว้หรือไม่ในบางเบราว์เซอร์ แต่หากไม่มีการเรียกกลับที่โหลดคุณจะไม่ต้องรอสักครู่เพื่อให้เบราว์เซอร์มีโอกาสเริ่มต้นคำขอ HTTP สำหรับรูปภาพนั้น
ChaseMoskal

นี่เป็นเพียงการพูดอะไรบางอย่าง!
Hitmands

4
การโหลดรูปภาพเป็นแบบอะซิงโครนัส
emil.c

@ emil.c ไม่เสมอไปด้วยเหตุผลบางประการ ChaseMoskal (ซึ่ง SO จะไม่ให้ฉันแจ้ง ...
tristan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.