ฉันสามารถเรียกใช้ javascript ก่อนที่จะโหลดทั้งหน้าได้หรือไม่


100

ฉันต้องการเรียกใช้ javascript ก่อนที่ทั้งหน้าจะโหลด เป็นไปได้หรือไม่ หรือโค้ดเริ่มทำงานบน</html>?



2
ฉันไม่คิดว่ามันซ้ำกัน แต่เป็นคำถามเกี่ยวกับการสื่อสารระหว่างเซิร์ฟเวอร์กับไคลเอนต์มากกว่า
scunliffe

คำตอบ:


190

ไม่เพียง แต่สามารถคุณ แต่คุณต้องให้ความพิเศษที่ไม่ได้ถ้าคุณไม่ต้องการที่จะ :-)

เมื่อเบราว์เซอร์พบscriptแท็กแบบคลาสสิกเมื่อแยกวิเคราะห์ HTML เบราว์เซอร์จะหยุดการแยกวิเคราะห์และส่งต่อไปยังตัวแปล JavaScript ซึ่งเรียกใช้สคริปต์ ตัวแยกวิเคราะห์จะไม่ดำเนินการต่อจนกว่าการเรียกใช้สคริปต์จะเสร็จสมบูรณ์ (เนื่องจากสคริปต์อาจdocument.writeเรียกไปยังมาร์กอัปเอาต์พุตที่ตัวแยกวิเคราะห์ควรจัดการ)

นั่นเป็นลักษณะการทำงานเริ่มต้น แต่คุณมีทางเลือกในการชะลอการเรียกใช้สคริปต์:

  1. ใช้โมดูล JavaScript type="module"สคริปต์รอการตัดบัญชีจนกว่า HTML ได้รับการแยกวิเคราะห์อย่างเต็มที่และ DOM เริ่มต้นสร้าง นี่ไม่ใช่เหตุผลหลักในการใช้โมดูล แต่เป็นสาเหตุหนึ่ง:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>
    

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

    สิ่งนี้ใช้ไม่ได้เมื่อฉันเขียนคำตอบนี้ครั้งแรกในปี 2010 แต่ที่นี่ในปี 2020 เบราว์เซอร์สมัยใหม่หลัก ๆ ทั้งหมดรองรับโมดูลโดยกำเนิดและหากคุณต้องการรองรับเบราว์เซอร์รุ่นเก่าคุณสามารถใช้บันเดิลเลอร์เช่น Webpack และ Rollup.js ได้

  2. ใช้deferแอตทริบิวต์บนแท็กสคริปต์คลาสสิก:

    <script defer src="./my-code.js"></script>
    

    เช่นเดียวกับโมดูลโค้ดในmy-code.jsจะถูกดึงและแยกวิเคราะห์ควบคู่ไปกับการแยกวิเคราะห์ HTML แต่จะไม่ถูกเรียกใช้จนกว่าการแยกวิเคราะห์ HTML จะเสร็จสิ้น แต่ , ไม่ได้ทำงานกับเนื้อหาสคริปต์แบบอินไลน์เท่านั้นที่มีไฟล์ภายนอกอ้างอิงทางdefersrc

  3. ฉันไม่คิดว่าเป็นสิ่งที่คุณต้องการ แต่คุณสามารถใช้asyncแอตทริบิวต์เพื่อบอกให้เบราว์เซอร์ดึงโค้ด JavaScript ควบคู่ไปกับการแยกวิเคราะห์ HTML แต่ให้เรียกใช้โดยเร็วที่สุดแม้ว่าการแยกวิเคราะห์ HTML จะไม่สมบูรณ์ . คุณสามารถวางไว้บนtype="module"แท็กหรือใช้แทนแท็กdeferแบบคลาสสิscript

  4. วางscriptแท็กไว้ท้ายเอกสารก่อน</body>แท็กปิด:

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>
    

    ด้วยวิธีนี้แม้ว่าโค้ดจะถูกเรียกใช้ทันทีที่พบ แต่องค์ประกอบทั้งหมดที่กำหนดโดย HTML ด้านบนก็มีอยู่และพร้อมที่จะใช้

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

ข้อมูลจำเพาะมีแผนภาพประโยชน์แสดงดิบscriptแท็กdefer, async, type="module"และtype="module" asyncและระยะเวลาของเมื่อรหัส JavaScript เป็นความจริงและการทำงาน:

ป้อนคำอธิบายภาพที่นี่

นี่คือตัวอย่างของพฤติกรรมเริ่มต้นscriptแท็กดิบ:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(ดูคำตอบของฉันที่นี่สำหรับรายละเอียดเกี่ยวกับNodeListรหัสนั้น)

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

ในทางตรงกันข้ามนี่คือtype="module"สคริปต์:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

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

(มีความจำเป็นในการไม่ได้NodeListตรวจสอบเพราะมีเบราว์เซอร์ที่ทันสมัยสนับสนุนโมดูลที่มีอยู่แล้วforEachบนNodeList.)

ในโลกสมัยใหม่นี้ไม่มีค่าที่แท้จริงสำหรับDOMContentLoadedเหตุการณ์ของคุณลักษณะ "พร้อม" ที่ PrototypeJS, jQuery, ExtJS, Dojo และอื่น ๆ ส่วนใหญ่ให้กลับมาในวันนั้น (และยังคงให้) เพียงใช้โมดูลหรือdefer. แม้ย้อนกลับไปในวันนั้นก็ไม่มีเหตุผลในการใช้งานมากนัก (และมักใช้อย่างไม่ถูกต้องถือการนำเสนอหน้าในขณะที่ไลบรารี jQuery ทั้งหมดถูกโหลดเนื่องจากscriptอยู่ในheadแทนที่จะอยู่หลังเอกสาร) นักพัฒนาบางคน Googleตั้งค่าสถานะไว้ก่อน นี่เป็นส่วนหนึ่งของเหตุผลที่แนะนำให้ YUIใส่สคริปต์ไว้ตอนท้ายของวันbodyนี้อีกครั้ง


<body onload="doIt()>"หรือวางไว้ในการทำงานและวางไว้ใน
Justin Liu

@JustinLiu - loadเหตุการณ์เกิดขึ้นช้ามากในรอบการโหลดหน้าเว็บแทบจะไม่เคยเป็นแนวทางปฏิบัติที่ดีที่สุดในการรอให้เรียกใช้ JavaScript จนกว่าจะถึงเวลานั้น แต่ใช่มันเป็นตัวเลือก ฉันได้แก้ไขคำตอบสำหรับปี 2020 แล้ว btw
TJ Crowder

หรือจะใช้document.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu

@JustinLiu - ใช่ดูย่อหน้าสุดท้าย :-) ฉันไม่เคยเห็นความจำเป็นใด ๆ แต่คุณทำได้
TJ Crowder

@JustinLiu - นี่คือจุดเริ่มต้นที่จะเหนือกว่า หากคุณต้องการโพสต์คำตอบโปรดโพสต์คำตอบ
TJ Crowder

6

คุณสามารถเรียกใช้โค้ดจาวาสคริปต์ได้ตลอดเวลา AFAIK จะดำเนินการในขณะที่เบราว์เซอร์มาถึงแท็ก <script> ที่อยู่ แต่คุณไม่สามารถเข้าถึงองค์ประกอบที่ยังไม่ได้โหลด

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


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