จะทราบได้อย่างไรว่าแท็ก <script> ไม่สามารถโหลดได้


112

ฉันเพิ่มแบบไดนามิก <script>แท็กในเพจ<head>และฉันอยากจะบอกได้ว่าการโหลดล้มเหลวหรือไม่ - 404 ข้อผิดพลาดของสคริปต์ในสคริปต์ที่โหลดหรืออะไรก็ตาม

ใน Firefox ใช้งานได้:

var script_tag = document.createElement('script');
script_tag.setAttribute('type', 'text/javascript');
script_tag.setAttribute('src', 'http://fail.org/nonexistant.js');
script_tag.onerror = function() { alert("Loading failed!"); }
document.getElementsByTagName('head')[0].appendChild(script_tag);

อย่างไรก็ตามสิ่งนี้ใช้ไม่ได้ใน IE หรือ Safari

มีใครรู้วิธีทำให้ใช้งานได้ในเบราว์เซอร์อื่นที่ไม่ใช่ Firefox บ้าง?

(ฉันไม่คิดว่าวิธีแก้ปัญหาที่ต้องวางโค้ดพิเศษในไฟล์. js เป็นวิธีที่ดีมันไม่สง่างามและไม่ยืดหยุ่น)


4
เว็บไซต์ขนาดใหญ่การโหลดการอ้างอิงโดยอัตโนมัติสำหรับเนื้อหาที่โหลดผ่าน ajax if+ การสำรวจเป็นสิ่งที่น่ารำคาญที่ฉันไม่ต้องการใส่ลงใน JS ทั้งหมด
เดวิด

2
คุณอาจต้องการตรวจสอบความล้มเหลวในการโหลดเมื่อสร้างคำขอ JSONP โดยใช้การแทรกแท็กสคริปต์ ...
schellsan

2
ข้ามไปที่ ans stackoverflow.com/questions/538745/…
Pacerier

คำตอบ:


36

ไม่มีเหตุการณ์ข้อผิดพลาดสำหรับแท็กสคริปต์ คุณสามารถบอกได้ว่าเมื่อใดที่สำเร็จและสมมติว่าไม่ได้โหลดหลังจากหมดเวลา:

<script type="text/javascript" onload="loaded=1" src="....js"></script>

7
ตัวฟัง "onload" จะเริ่มทำงานแม้ว่าจาวาสคริปต์จะมีข้อผิดพลาดก็ตาม
Luca Matteis

38
ใช่ถ้าไฟล์ LOADS และมีข้อผิดพลาดในตัวไฟล์ แต่ถ้าไฟล์ไม่ได้รับการโหลดออนโหลดจะไม่เริ่มทำงาน
Diodeus - James MacFarlane

1
ใช่จริงนั่นเป็นเหตุผลที่ฉันขอให้เจาะจงมากขึ้นว่าเขากำลังมองหาข้อผิดพลาดประเภทใด
Luca Matteis

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

6
มี onerror evnt สำหรับแท็กสคริปต์ จะเริ่มทำงานเมื่อไม่พบทรัพยากร
Nguyen Tran

24

หากคุณสนใจเฉพาะเบราว์เซอร์ html5 คุณสามารถใช้เหตุการณ์ข้อผิดพลาดได้ (เนื่องจากเป็นเพียงการจัดการข้อผิดพลาดเท่านั้นจึงควรสนับสนุนสิ่งนี้ในเบราว์เซอร์รุ่นถัดไปสำหรับ KISS IMHO เท่านั้น)

จากข้อมูลจำเพาะ:

หากค่าของแอตทริบิวต์ src เป็นสตริงว่างหรือไม่สามารถแก้ไขได้ตัวแทนผู้ใช้จะต้องจัดคิวงานเพื่อเริ่มต้นเหตุการณ์ธรรมดาที่ชื่อข้อผิดพลาดที่องค์ประกอบและยกเลิกขั้นตอนเหล่านี้

~

หากการโหลดส่งผลให้เกิดข้อผิดพลาด (เช่นข้อผิดพลาด DNS หรือข้อผิดพลาด HTTP 404) การดำเนินการบล็อกสคริปต์ต้องประกอบด้วยการเริ่มต้นเหตุการณ์ง่ายๆที่ชื่อข้อผิดพลาดที่องค์ประกอบ

ซึ่งหมายความว่าคุณไม่ต้องทำการสำรวจข้อผิดพลาดใด ๆ และสามารถรวมเข้ากับแอตทริบิวต์ async และ defer เพื่อให้แน่ใจว่าสคริปต์ไม่ได้บล็อกการแสดงผลหน้า:

อาจมีการระบุแอตทริบิวต์ defer แม้ว่าจะระบุแอตทริบิวต์ async เพื่อทำให้เว็บเบราว์เซอร์เดิมที่สนับสนุนเฉพาะ defer (และไม่ async) ถอยกลับไปใช้พฤติกรรม defer แทนพฤติกรรมการบล็อกแบบซิงโครนัสที่เป็นค่าเริ่มต้น

เพิ่มเติมได้ที่http://www.w3.org/TR/html5/scripting-1.html#script


1
ความคิดเกี่ยวกับการใช้งาน JSONP เช่นนั้นใน jQuery สนับสนุนสิ่งนี้หรือไม่? ทุกสิ่งที่ฉันได้อ่านที่นี่เกี่ยวกับการตรวจพบว่าความล้มเหลวในการโหลด JSONP บอกว่าไม่สามารถตรวจพบได้ แต่การหมดเวลาอาจเป็นวิธีแก้ปัญหาชั่วคราวและการหมดเวลาถูกเพิ่มลงใน JSONP เวอร์ชันล่าสุด ดูเหมือนว่าคำตอบนี้จะให้สิ่งที่ดีกว่าการหมดเวลา - ถูกต้องหรือไม่?
hippietrail

1
ตัวอย่างได้โปรด?
rogerdpack

13
<script src="nonexistent.js" onerror="alert('error!')"></script> ซอ
Rudey

@Rudey Uncaught SyntaxError: Unexpected token '<'(ใน Chrome ล่าสุด)
daleyjem

@daleyjem ดูเหมือนว่าคุณพยายามใส่แท็ก HTML ใน JavaScript <script src="nonexistent.js" onerror="alert('error!')"></script>ควรอยู่ในไฟล์ HTML ของคุณไม่ใช่ JS
Rudey

21

สคริปต์จาก Erwinus ใช้งานได้ดี แต่ไม่มีการเข้ารหัสอย่างชัดเจน ฉันใช้เสรีภาพในการทำความสะอาดและถอดรหัสว่ามันกำลังทำอะไร ฉันได้ทำการเปลี่ยนแปลงเหล่านี้:

  • ชื่อตัวแปรที่มีความหมาย
  • การใช้prototype.
  • require() ใช้ตัวแปรอาร์กิวเมนต์
  • ไม่มีการalert()ส่งคืนข้อความตามค่าเริ่มต้น
  • แก้ไขข้อผิดพลาดทางไวยากรณ์และปัญหาขอบเขตที่ฉันได้รับ

ขอขอบคุณอีกครั้งสำหรับ Erwinus การทำงานของตัวเองเป็นจุดเริ่มต้น

function ScriptLoader() {
}

ScriptLoader.prototype = {

    timer: function (times, // number of times to try
                     delay, // delay per try
                     delayMore, // extra delay per try (additional to delay)
                     test, // called each try, timer stops if this returns true
                     failure, // called on failure
                     result // used internally, shouldn't be passed
            ) {
        var me = this;
        if (times == -1 || times > 0) {
            setTimeout(function () {
                result = (test()) ? 1 : 0;
                me.timer((result) ? 0 : (times > 0) ? --times : times, delay + ((delayMore) ? delayMore : 0), delayMore, test, failure, result);
            }, (result || delay < 0) ? 0.1 : delay);
        } else if (typeof failure == 'function') {
            setTimeout(failure, 1);
        }
    },

    addEvent: function (el, eventName, eventFunc) {
        if (typeof el != 'object') {
            return false;
        }

        if (el.addEventListener) {
            el.addEventListener(eventName, eventFunc, false);
            return true;
        }

        if (el.attachEvent) {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    },

    // add script to dom
    require: function (url, args) {
        var me = this;
        args = args || {};

        var scriptTag = document.createElement('script');
        var headTag = document.getElementsByTagName('head')[0];
        if (!headTag) {
            return false;
        }

        setTimeout(function () {
            var f = (typeof args.success == 'function') ? args.success : function () {
            };
            args.failure = (typeof args.failure == 'function') ? args.failure : function () {
            };
            var fail = function () {
                if (!scriptTag.__es) {
                    scriptTag.__es = true;
                    scriptTag.id = 'failed';
                    args.failure(scriptTag);
                }
            };
            scriptTag.onload = function () {
                scriptTag.id = 'loaded';
                f(scriptTag);
            };
            scriptTag.type = 'text/javascript';
            scriptTag.async = (typeof args.async == 'boolean') ? args.async : false;
            scriptTag.charset = 'utf-8';
            me.__es = false;
            me.addEvent(scriptTag, 'error', fail); // when supported
            // when error event is not supported fall back to timer
            me.timer(15, 1000, 0, function () {
                return (scriptTag.id == 'loaded');
            }, function () {
                if (scriptTag.id != 'loaded') {
                    fail();
                }
            });
            scriptTag.src = url;
            setTimeout(function () {
                try {
                    headTag.appendChild(scriptTag);
                } catch (e) {
                    fail();
                }
            }, 1);
        }, (typeof args.delay == 'number') ? args.delay : 1);
        return true;
    }
};

$(document).ready(function () {
    var loader = new ScriptLoader();
    loader.require('resources/templates.js', {
        async: true, success: function () {
            alert('loaded');
        }, failure: function () {
            alert('NOT loaded');
        }
    });
});

4
ฉันถูกบังคับให้ใช้$ .getScriptของ jQuery เนื่องจากฟังก์ชั่นนี้ล้มเหลวสำหรับสคริปต์ที่แคชใน MSIE8- โชคไม่ดี
Zathrus Writer

@ZathrusWriter นั่นน่าจะเป็นความคิดที่ดีกว่าขอบคุณที่แจ้งให้เราทราบ! คุณอาจเพิ่ม int แบบสุ่มให้กับ url ในโค้ดนี้และมันจะลบการแคช
Aram Kocharyan

2
ใช่ Aram นั่นจะทำเคล็ดลับอย่างแน่นอนอย่างไรก็ตามมันจะทำให้การแคชของเบราว์เซอร์เป็นโมฆะด้วยดังนั้นค่าใช้จ่ายในกรณีนี้อาจไม่คุ้มค่าจริงๆ;)
Zathrus Writer

@ZathrusWriter ดังนั้นการทำงานของการนำ jQuery ไปใช้งานอย่างไร
Pacerier

@Pacerier ขอโทษฉันไม่ใช่ผู้พัฒนาหลักของ jQuery ดังนั้นฉันจึงไม่สามารถตอบได้จริงๆ
Zathrus Writer

18

น้ำยาทำความสะอาดที่ใช้งานได้ของฉัน (2017)

function loaderScript(scriptUrl){
   return new Promise(function (res, rej) {
    let script = document.createElement('script');
    script.src = scriptUrl;
    script.type = 'text/javascript';
    script.onError = rej;
    script.async = true;
    script.onload = res;
    script.addEventListener('error',rej);
    script.addEventListener('load',res);
    document.head.appendChild(script);
 })

}

ตามที่มาร์ตินชี้ให้ใช้เช่นนั้น:

const event = loaderScript("myscript.js")
  .then(() => { console.log("loaded"); })
  .catch(() => { console.log("error"); });

หรือ

try{
 await loaderScript("myscript.js")
 console.log("loaded"); 
}catch{
 console.log("error");
}

1
ใช้ดังนี้:loaderScript("myscript.js").then(() => { console.log("loaded"); }).catch(() => { console.log("error"); });
Martin Wantke

17

ฉันรู้ว่านี่เป็นกระทู้เก่า แต่ฉันมีทางออกที่ดีสำหรับคุณ (ฉันคิดว่า) มันคัดลอกมาจากคลาสของฉันซึ่งจัดการกับสิ่งของ AJAX ทั้งหมด

เมื่อไม่สามารถโหลดสคริปต์ได้สคริปต์จะตั้งค่าตัวจัดการข้อผิดพลาด แต่เมื่อไม่รองรับตัวจัดการข้อผิดพลาดจะกลับไปที่ตัวจับเวลาที่ตรวจสอบข้อผิดพลาดเป็นเวลา 15 วินาที

function jsLoader()
{
    var o = this;

    // simple unstopable repeat timer, when t=-1 means endless, when function f() returns true it can be stopped
    o.timer = function(t, i, d, f, fend, b)
    {
        if( t == -1 || t > 0 )
        {
            setTimeout(function() {
                b=(f()) ? 1 : 0;
                o.timer((b) ? 0 : (t>0) ? --t : t, i+((d) ? d : 0), d, f, fend,b );
            }, (b || i < 0) ? 0.1 : i);
        }
        else if(typeof fend == 'function')
        {
            setTimeout(fend, 1);
        }
    };

    o.addEvent = function(el, eventName, eventFunc)
    {
        if(typeof el != 'object')
        {
            return false;
        }

        if(el.addEventListener)
        {
            el.addEventListener (eventName, eventFunc, false);
            return true;
        }

        if(el.attachEvent)
        {
            el.attachEvent("on" + eventName, eventFunc);
            return true;
        }

        return false;
    };

    // add script to dom
    o.require = function(s, delay, baSync, fCallback, fErr)
    {
        var oo = document.createElement('script'),
        oHead = document.getElementsByTagName('head')[0];
        if(!oHead)
        {
            return false;
        }

        setTimeout( function() {
            var f = (typeof fCallback == 'function') ? fCallback : function(){};
            fErr = (typeof fErr == 'function') ? fErr : function(){
                alert('require: Cannot load resource -'+s);
            },
            fe = function(){
                if(!oo.__es)
                {
                    oo.__es = true;
                    oo.id = 'failed';
                    fErr(oo);
                }
            };
            oo.onload = function() {
                oo.id = 'loaded';
                f(oo);
            };
            oo.type = 'text/javascript';
            oo.async = (typeof baSync == 'boolean') ? baSync : false;
            oo.charset = 'utf-8';
            o.__es = false;
            o.addEvent( oo, 'error', fe ); // when supported

            // when error event is not supported fall back to timer
            o.timer(15, 1000, 0, function() {
                return (oo.id == 'loaded');
            }, function(){ 
                if(oo.id != 'loaded'){
                    fe();
                }
            });
            oo.src = s;
            setTimeout(function() {
                try{
                    oHead.appendChild(oo);
                }catch(e){
                    fe();
                }
            },1); 
        }, (typeof delay == 'number') ? delay : 1);  
        return true;
    };

}

$(document).ready( function()
{
    var ol = new jsLoader();
    ol.require('myscript.js', 800, true, function(){
        alert('loaded');
    }, function() {
        alert('NOT loaded');
    });
});

4
... jQuery มาจากไหน?
Naftali aka Neal

1
และยังเป็นโซลูชันข้ามเบราว์เซอร์ ;-)
Codebeat

1
@Erwinus ฉันไม่ได้ตรวจสอบส่วนหัวมันเป็นเพียงการตรวจสอบข้ามเบราว์เซอร์อย่างรวดเร็วที่ฉันทำและ$ .getScriptก็ทำงานโดยไม่มีปัญหาเสมอดังนั้นฉันจึงติดอยู่กับมัน ... คุณสามารถลองด้วยตัวเองฉันใช้ W7 และ XAMPP ที่นี่
Zathrus Writer

19
ไม่ว่าจะได้ผลหรือไม่มันก็เจ็บปวดที่ต้องอ่าน - นับประสาการแก้ไขข้อบกพร่อง นั่นเป็นรูปแบบที่ค่อนข้างแย่และสไตล์การเข้ารหัสที่โหดร้ายอย่างแท้จริง การตั้งชื่อตัวแปรเพียงอย่างเดียวเป็นเรื่องยุ่ง
Thor84no

7
หากคุณไม่เห็นคุณค่าของรหัสที่อ่านได้ (และคุณสามารถทำให้อ่านได้โดยไม่มีการเปลี่ยนแปลงใด ๆ กับโค้ดที่เรียกใช้งานจริง) ฉันรู้สึกเสียใจกับทุกคนที่ต้องทำงานร่วมกับคุณและรหัสของคุณ
Thor84no

10

หากต้องการตรวจสอบว่า javascript ในการnonexistant.jsส่งคืนไม่มีข้อผิดพลาดคุณต้องเพิ่มตัวแปรภายในhttp://fail.org/nonexistant.jslike var isExecuted = true;จากนั้นตรวจสอบว่ามีอยู่หรือไม่เมื่อโหลดแท็กสคริปต์

อย่างไรก็ตามหากคุณต้องการตรวจสอบว่าสิ่งที่nonexistant.jsส่งคืนโดยไม่มี 404 (หมายความว่ามีอยู่) คุณสามารถลองใช้isLoadedตัวแปร ...

var isExecuted = false;
var isLoaded = false;
script_tag.onload = script_tag.onreadystatechange = function() {
    if(!this.readyState ||
        this.readyState == "loaded" || this.readyState == "complete") {
        // script successfully loaded
        isLoaded = true;

        if(isExecuted) // no error
    }
}

ซึ่งจะครอบคลุมทั้งสองกรณี


1
มันใช้งานได้ แต่ไม่ได้ทำในสิ่งที่ฉันต้องการจริงๆ - ความล้มเหลวในกรณีนั้นจะไม่ทำให้เหตุการณ์เกิดขึ้น ฉันไม่ต้องการสำรวจตัวแปรและหากยังคงเป็นเท็จหลังจากผ่านไปสองสามวินาทีให้เริ่มการโทรกลับเมื่อล้มเหลว
เดวิด

คุณพยายามดึงข้อผิดพลาดประเภทใด โหลดไม่สำเร็จ? ล้มเหลวในการเรียกใช้จาวาสคริปต์ใน nonexstant.js? ข้อผิดพลาดในการตอบสนอง HTTP? กรุณาอธิบายเพิ่มเติม
Luca Matteis

อย่างใดอย่างหนึ่งข้างต้นจะดี ฉันสนใจเป็นพิเศษเกี่ยวกับข้อผิดพลาด 404 แม้ว่า
เดวิด

404 หมายความว่าคุณต้องการตรวจสอบว่าไม่มีไฟล์ nonexistant.js หรือไม่? แต่ถ้าคุณต้องการตรวจสอบว่าไม่มีข้อผิดพลาดหรือไม่?
Luca Matteis

เฮ้เดวิดคุณควรจะสามารถสันนิษฐานได้ว่าถ้า this.readyState / ... ไม่เป็นจริงแสดงว่าสคริปต์ไม่สามารถโหลดได้ โดยทั่วไปแล้ว onError
Cody

7

ฉันหวังว่านี่จะไม่ได้รับการโหวตลดลงเพราะในสถานการณ์พิเศษมันเป็นวิธีที่น่าเชื่อถือที่สุดในการแก้ปัญหา ทุกครั้งที่เซิร์ฟเวอร์อนุญาตให้คุณรับทรัพยากร Javascript โดยใช้ CORS ( http://en.wikipedia.org/wiki/Cross-origin_resource_sharing ) คุณจะมีตัวเลือกมากมายให้ทำเช่นนั้น

การใช้ XMLHttpRequest เพื่อดึงทรัพยากรจะใช้ได้กับเบราว์เซอร์สมัยใหม่ทั้งหมดรวมถึง IE เนื่องจากคุณต้องการโหลด Javascript คุณจึงมี Javascript ให้ใช้งานได้ตั้งแต่แรก คุณสามารถติดตามความคืบหน้าโดยใช้ readyState ( http://en.wikipedia.org/wiki/XMLHttpRequest#The_onreadystatechange_event_listener ) สุดท้ายเมื่อคุณได้รับเนื้อหาของไฟล์คุณสามารถเรียกใช้งานได้โดยใช้ eval () ใช่ฉันพูดว่า eval - เพราะการรักษาความปลอดภัยมันไม่ต่างจากการโหลดสคริปต์ตามปกติ ในความเป็นจริงเทคนิคที่คล้ายกันนี้แนะนำโดย John Resig เพื่อให้มีแท็กที่ดีกว่า ( http://ejohn.org/blog/degrading-script-tags/ )

วิธีนี้ยังช่วยให้คุณแยกการโหลดออกจาก eval และเรียกใช้ฟังก์ชันก่อนและหลัง eval เกิดขึ้น จะมีประโยชน์มากเมื่อโหลดสคริปต์แบบขนาน แต่ประเมินทีละรายการ - บางอย่างเบราว์เซอร์สามารถทำได้อย่างง่ายดายเมื่อคุณวางแท็กใน HTML แต่อย่าให้คุณเพิ่มสคริปต์ในขณะทำงานด้วย Javascript

CORS ยังนิยมใช้ JSONP สำหรับการโหลดสคริปต์ ( http://en.wikipedia.org/wiki/XMLHttpRequest#Cross-domain_requests ) อย่างไรก็ตามหากคุณกำลังพัฒนาวิดเจ็ตของบุคคลที่สามของคุณเองเพื่อฝังในไซต์อื่นคุณควรโหลดไฟล์ Javascript จากโดเมนของคุณเองใน iframe ของคุณเอง (อีกครั้งโดยใช้ AJAX)

ในระยะสั้น:

  1. ลองดูว่าคุณสามารถโหลดทรัพยากรโดยใช้ AJAX GET ได้หรือไม่

  2. ใช้ eval หลังจากโหลดสำเร็จแล้ว

ในการปรับปรุง:

  1. ตรวจสอบส่วนหัวการควบคุมแคชที่กำลังส่ง

  2. ตรวจสอบการแคชเนื้อหาใน localStorage หากคุณต้องการ

  3. ลองดู "จาวาสคริปต์ที่ลดระดับ" ของ Resig เพื่อทำความสะอาดโค้ด

  4. ตรวจสอบ require.js


หมายเหตุด้วยวิธีนี้ต้องการให้เซิร์ฟเวอร์ต้นทางเปิดใช้งาน CORS ซึ่งไม่ได้เป็นเช่นนั้นเสมอไปหรือควบคุมได้: |
rogerdpack

5

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

อย่างไรก็ตามนี่คือวิธีแก้ปัญหา: 1) เริ่มต้นตัวแปรเป็นเท็จ 2) ตั้งค่าเป็น true เมื่อจาวาสคริปต์โหลด (โดยใช้แอตทริบิวต์ onload) 3) ตรวจสอบว่าตัวแปรเป็นจริงหรือเท็จเมื่อโหลดเนื้อหา HTML แล้ว

<html>
  <head>
    <script>
      var scriptLoaded = false;

      function checkScriptLoaded() {
        if (scriptLoaded) {
          // do something here
        } else {
          // do something else here!
        }
      }
    </script>
    <script src="http://some-external-script.js" onload="scriptLoaded=true;" />
  </head>
  <body onload="checkScriptLoaded()">
    <p>My Test Page!</p>
  </body>
</html>

1
จะเกิดอะไรขึ้นถ้าสคริปต์ยังคงโหลดอยู่? คุณจะรู้ได้อย่างไรว่าไม่พบไฟล์หรือถ้าคุณต้องรอ?
OSA

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

ทางออกที่ดี ฉันไม่เข้าใจว่าทำไมถึงไม่ได้รับการโหวต
Lotfi

สิ่งนี้ถือว่าสคริปต์จะถูกโหลดพร้อมกันเพื่อบล็อกทุกอย่างอื่นซึ่งไม่เป็นความจริงเสมอไป อันที่จริงมันไม่ใช่วิธีที่แนะนำในการโหลดสคริปต์
Vitim.us

4

นี่คือโซลูชันที่ใช้ JQuery โดยไม่มีตัวจับเวลาใด ๆ :

<script type="text/javascript">
function loadScript(url, onsuccess, onerror) {
$.get(url)
    .done(function() {
        // File/url exists
        console.log("JS Loader: file exists, executing $.getScript "+url)
        $.getScript(url, function() {
            if (onsuccess) {
                console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
                onsuccess();
                console.log("JS Loader: done with onsuccess() for " + url);
            } else {
                console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
            }
        });
    }).fail(function() {
            // File/url does not exist
            if (onerror) {
                console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
                onerror();
                console.error("JS Loader: done with onerror() for " + url);
            } else {
                console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
            }
    });
}
</script>

ขอบคุณข้อมูล: https://stackoverflow.com/a/14691735/1243926

ตัวอย่างการใช้งาน (ตัวอย่างต้นฉบับจากเอกสาร JQuery getScript ):

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>jQuery.getScript demo</title>
  <style>
  .block {
     background-color: blue;
     width: 150px;
     height: 70px;
     margin: 10px;
  }
  </style>
  <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
</head>
<body>

<button id="go">&raquo; Run</button>
<div class="block"></div>

<script>


function loadScript(url, onsuccess, onerror) {
$.get(url)
    .done(function() {
        // File/url exists
        console.log("JS Loader: file exists, executing $.getScript "+url)
        $.getScript(url, function() {
            if (onsuccess) {
                console.log("JS Loader: Ok, loaded. Calling onsuccess() for " + url);
                onsuccess();
                console.log("JS Loader: done with onsuccess() for " + url);
            } else {
                console.log("JS Loader: Ok, loaded, no onsuccess() callback " + url)
            }
        });
    }).fail(function() {
            // File/url does not exist
            if (onerror) {
                console.error("JS Loader: probably 404 not found. Not calling $.getScript. Calling onerror() for " + url);
                onerror();
                console.error("JS Loader: done with onerror() for " + url);
            } else {
                console.error("JS Loader: probably 404 not found. Not calling $.getScript. No onerror() callback " + url);
            }
    });
}


loadScript("https://raw.github.com/jquery/jquery-color/master/jquery.color.js", function() {
  console.log("loaded jquery-color");
  $( "#go" ).click(function() {
    $( ".block" )
      .animate({
        backgroundColor: "rgb(255, 180, 180)"
      }, 1000 )
      .delay( 500 )
      .animate({
        backgroundColor: "olive"
      }, 1000 )
      .delay( 500 )
      .animate({
        backgroundColor: "#00f"
      }, 1000 );
  });
}, function() { console.error("Cannot load jquery-color"); });


</script>
</body>
</html>

เซอร์เกย์ที่ดี! ดังนั้นคุณกำลังใช้. get () เพื่อตรวจสอบว่ามีไฟล์อยู่หรือไม่และ. getScript () เพื่อตรวจสอบว่าสามารถเรียกใช้งาน / โหลดได้โดยไม่มีข้อผิดพลาดหรือไม่?
jjwdesign

2
ใช่. ตามที่ฉันจำได้วิธีนี้มีข้อ จำกัด : คุณจะไม่สามารถโหลดสคริปต์จำนวนมากด้วยวิธีนี้ได้ --- ถ้าฉันจำไม่ผิดเนื่องจากข้อ จำกัด การเขียนสคริปต์ข้ามโดเมน
osa

ปัญหาในการใช้ jQuery (หรือไลบรารีอื่น) คือคุณต้องโหลดไลบรารีนั้นก่อน คุณจะตรวจสอบได้อย่างไรว่าไลบรารีนั้นไม่สามารถโหลดได้?
Pacerier

ฉันได้ใช้แนวทางนี้เช่นกัน แต่ฉันแนะนำให้ใช้cache:trueสำหรับการเรียก $ .getScript (ซึ่งจะปิดโดยค่าเริ่มต้น) ด้วยวิธีนี้สำหรับคำขอส่วนใหญ่จะใช้เวอร์ชันแคชโดยอัตโนมัติสำหรับการโทร getScript (ช่วยประหยัดเวลาในการตอบสนอง)
Laurens Rietveld

2

สิ่งนี้สามารถทำได้อย่างปลอดภัยโดยใช้คำสัญญา

function loadScript(src) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => resolve(script);
    script.onerror = () => reject(new Error("Script load error: " + src));

    document.head.append(script);
  });
}

และใช้แบบนี้

let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js");

promise.then(
  script => alert(`${script.src} is loaded!`),
  error => alert(`Error: ${error.message}`)
);

1

สาเหตุที่ใช้ไม่ได้ใน Safari เนื่องจากคุณใช้ไวยากรณ์แอตทริบิวต์ สิ่งนี้จะใช้ได้ดีแม้ว่า:

script_tag.addEventListener('error', function(){/*...*/}, true);

... ยกเว้นใน IE

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


นั่นไม่ได้สร้างความแตกต่างใด ๆ สำหรับ Safari แต่ก็ยังไม่เริ่มการทำงานของตัวจัดการข้อผิดพลาด
เดวิด

ฉันมีปัญหากับตัวจัดการ onclick ในซาฟารีและนี่คือสิ่งที่แก้ไขได้ดังนั้นฉันจึงคิดว่ามันก็เหมือนกันสำหรับ onerror เช่นกัน เห็นได้ชัดว่าไม่ใช่ ...

1
สิ่งนี้ควรใช้กับแท็กสคริปต์ที่รวมแบบสแตติกด้วยหรือแค่ฉีดแบบไดนามิก ฉันเพิ่งพยายามใช้เพื่อตรวจสอบว่า jQuery ไม่สามารถโหลดได้ แต่ไม่ทำงาน ฉันไม่แน่ใจว่าฉันทำผิดหรือไม่ได้ผลในกรณีนี้
hippietrail

1
@hippietrail - ฟังเหตุการณ์จะไม่ทำงานในคง <script>HTML หากคุณพยายามเพิ่มก่อนหน้านี้คุณจะได้รับข้อผิดพลาดว่าไม่มีแท็กและหากคุณเพิ่มหลังจากนั้นสคริปต์ก็ทำงานและทริกเกอร์เหตุการณ์แล้ว วิธีเดียวคือมองหาผลข้างเคียงที่เกิดจากสคริปต์

ขอบคุณครับ น่าเสียดายที่ใช้งานได้กับ 404 เท่านั้นไม่ใช่สำหรับข้อผิดพลาด JS การตั้งค่าตัวแปรภายในสคริปต์ไม่สามารถทำได้เสมอไป
Jo Liss

1

เหตุการณ์ onerror

* อัปเดตเดือนสิงหาคม 2017: Chrome และ Firefox ทำงานผิดพลาด onload ถูกเรียกใช้โดย Internet Explorer Edge ไม่ยิงทั้ง onerror หรือ onload ฉันจะไม่ใช้วิธีนี้ แต่ก็ใช้ได้ในบางกรณี ดูสิ่งนี้ด้วย

<link> onerror ไม่ทำงานใน IE

*

คำจำกัดความและการใช้งาน เหตุการณ์ onerror จะถูกทริกเกอร์หากเกิดข้อผิดพลาดขณะโหลดไฟล์ภายนอก (เช่นเอกสารหรือรูปภาพ)

เคล็ดลับ:เมื่อใช้กับสื่อเสียง / วิดีโอเหตุการณ์ที่เกี่ยวข้องที่เกิดขึ้นเมื่อมีสิ่งรบกวนกระบวนการโหลดสื่อ ได้แก่ :

  • onabort
  • ยกเว้น
  • ติดตั้ง
  • onsuspend

ใน HTML:

องค์ประกอบ onerror = "myScript">

ใน JavaScriptโดยใช้เมธอด addEventListener ():

object.addEventListener ("ข้อผิดพลาด", myScript);

หมายเหตุ:วิธีการ addEventListener () ไม่ได้รับการสนับสนุนใน Internet Explorer 8 และเวอร์ชันก่อนหน้า

ตัวอย่าง ดำเนินการ JavaScript หากเกิดข้อผิดพลาดขณะโหลดรูปภาพ:

img src = "image.gif" onerror = "myFunction ()">


0

มีการเสนอให้ตั้งค่าการหมดเวลาและถือว่าการโหลดล้มเหลวหลังจากหมดเวลา

setTimeout(fireCustomOnerror, 4000);

ปัญหาของแนวทางนั้นคือสมมติฐานขึ้นอยู่กับความบังเอิญ หลังจากหมดเวลาของคุณคำขอจะยังคงรอดำเนินการ คำขอสำหรับสคริปต์ที่รอดำเนินการอาจโหลดขึ้นแม้ว่าโปรแกรมเมอร์จะสันนิษฐานว่าการโหลดจะไม่เกิดขึ้นก็ตาม

หากสามารถยกเลิกคำขอได้โปรแกรมอาจรอระยะเวลาหนึ่งจากนั้นจึงยกเลิกคำขอ


0

นี่คือวิธีที่ฉันใช้คำสัญญาในการตรวจจับข้อผิดพลาดในการโหลดที่ปล่อยออกมาบนวัตถุหน้าต่าง:

<script type='module'>
window.addEventListener('error', function(error) {
  let url = error.filename
  url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#"));
  url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?"));
  url = url.substring(url.lastIndexOf("/") + 1, url.length);
  window.scriptLoadReject && window.scriptLoadReject[url] && window.scriptLoadReject[url](error);
}, true);
window.boot=function boot() {
  const t=document.createElement('script');
  t.id='index.mjs';
  t.type='module';
  new Promise((resolve, reject) => {
    window.scriptLoadReject = window.scriptLoadReject || {};
    window.scriptLoadReject[t.id] = reject;
    t.addEventListener('error', reject);
    t.addEventListener('load', resolve); // Careful load is sometimes called even if errors prevent your script from running! This promise is only meant to catch errors while loading the file.
  }).catch((value) => {
    document.body.innerHTML='Error loading ' + t.id + '! Please reload this webpage.<br/>If this error persists, please try again later.<div><br/>' + t.id + ':' + value.lineno + ':' + value.colno + '<br/>' + (value && value.message);
  });
  t.src='./index.mjs'+'?'+new Date().getTime();
  document.head.appendChild(t);
};
</script>
<script nomodule>document.body.innerHTML='This website needs ES6 Modules!<br/>Please enable ES6 Modules and then reload this webpage.';</script>
</head>

<body onload="boot()" style="margin: 0;border: 0;padding: 0;text-align: center;">
  <noscript>This website needs JavaScript!<br/>Please enable JavaScript and then reload this webpage.</noscript>

0

วิธีเดียวที่ฉันคิดได้ว่าการทำทุกอย่างที่คุณต้องการนั้นค่อนข้างน่าเกลียด ขั้นแรกให้ทำการเรียก AJAX เพื่อดึงเนื้อหาไฟล์ Javascript เมื่อดำเนินการเสร็จสิ้นคุณสามารถตรวจสอบรหัสสถานะเพื่อตัดสินใจว่าการดำเนินการนี้สำเร็จหรือไม่ จากนั้นนำ responseText จากอ็อบเจ็กต์ xhr และรวมไว้ใน try / catch สร้างแท็กสคริปต์แบบไดนามิกและสำหรับ IE คุณสามารถตั้งค่าคุณสมบัติข้อความของแท็กสคริปต์เป็นข้อความ JS ในเบราว์เซอร์อื่น ๆ ทั้งหมดที่คุณควรจะทำได้ ผนวกโหนดข้อความที่มีเนื้อหาลงในแท็กสคริปต์ หากมีโค้ดใด ๆ ที่คาดว่าแท็กสคริปต์จะมีตำแหน่ง src ของไฟล์จริงๆสิ่งนี้จะใช้ไม่ได้ แต่ควรใช้ได้กับสถานการณ์ส่วนใหญ่


วิธีนี้ล้าสมัยไปแล้วสำหรับเบราว์เซอร์สมัยใหม่
Simon_Weaver
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.