innerHTML ไม่ตรงกันหรือไม่


103

ฉันหวังว่าฉันจะไม่ทำตัวโง่ ๆ แต่ฉันพยายามทำความเข้าใจว่าเกิดอะไรขึ้นในโค้ดสองบรรทัดนี้:

document.body.innerHTML = 'something';
alert('something else');

สิ่งที่ฉันสังเกตคือการแจ้งเตือนจะแสดงก่อนที่จะอัปเดต HTML (หรืออาจจะมี แต่หน้ายังไม่ได้รับการรีเฟรช / ทาสีใหม่ / อะไรก็ตาม)

ชำระเงินนี้codepenเพื่อดูสิ่งที่ผมหมายถึง

โปรดทราบว่าแม้แต่การใส่alertเข้าไปsetTimeout(..., 0)ก็ไม่ช่วยอะไร ดูเหมือนว่าจะต้องใช้เหตุการณ์วนซ้ำมากกว่าในinnerHTMLการอัปเดตเพจ

แก้ไข:

ฉันลืมบอกว่าฉันใช้ Chrome และไม่ได้ตรวจสอบเบราว์เซอร์อื่น ดูเหมือนว่าจะปรากฏเฉพาะใน Chrome เท่านั้น อย่างไรก็ตามฉันยังคงสนใจว่าเหตุใดจึงเกิดขึ้น


4
นี่เป็นเรื่องปกติสำหรับ Chrome
trincot

2
@trincot ขอบคุณ! ฉันลืมบอกว่าฉันใช้ Chrome และไม่ได้ลองใช้เบราว์เซอร์อื่นก่อนที่จะถาม คุณมีข้อมูลอ้างอิงหรือไม่?
apieceofbart

16
การเปลี่ยน DOM เป็นแบบซิงโครนัส การแสดงผล DOM เกิดขึ้นจริงหลังจากล้างสแต็ก JavaScript แล้ว Developers.google.com/web/fundamentals/performance/rendering JavaScript> Style> Layout> Paint> Composite (อย่างน้อยสำหรับ Chrome เบราว์เซอร์อื่น ๆ ก็คล้ายกัน)
เริ่ม

2
เพียงแค่การคาดเดา: การแจ้งเตือนกำลังบล็อกจะป้องกันไม่ให้เบราว์เซอร์ถึงขั้นตอนการทาสีใหม่ดังนั้นในขณะที่ DOM มีการเปลี่ยนแปลง แต่ก็ยังไม่ได้รับอนุญาตให้ทาสีใหม่ อีกครั้งเป็นเพียงการคาดเดา
zzzzBov

2
@qbolec ตรวจสอบวิดีโอนี้: youtu.be/r8caVE_a5KQ
apieceofbart

คำตอบ:


132

การตั้งค่า innerHTML เป็นแบบซิงโครนัสเช่นเดียวกับการเปลี่ยนแปลงส่วนใหญ่ที่คุณสามารถทำได้กับ DOM อย่างไรก็ตามการแสดงผลหน้าเว็บเป็นเรื่องที่แตกต่างกัน

(โปรดจำไว้ว่า DOM ย่อมาจาก "Document Object Model" เป็นเพียง "โมเดล" ซึ่งเป็นตัวแทนของข้อมูลสิ่งที่ผู้ใช้เห็นบนหน้าจอคือภาพว่าโมเดลนั้นควรมีลักษณะอย่างไรดังนั้นการเปลี่ยนโมเดลจึงไม่ได้เกิดขึ้นในทันที เปลี่ยนรูปภาพ - ต้องใช้เวลาสักพักในการอัปเดต)

การเรียกใช้ JavaScript และการแสดงผลหน้าเว็บเกิดขึ้นแยกกัน ที่จะนำมัน simplistically แรกทั้งหมดของ JavaScript บนหน้าวิ่ง (จากวงเหตุการณ์ - ตรวจสอบวิดีโอที่ยอดเยี่ยมนี้เพื่อดูรายละเอียดเพิ่มเติม) แล้วหลังจากที่เบราว์เซอร์วาทการเปลี่ยนแปลงใด ๆ ไปยังหน้าเว็บสำหรับผู้ใช้เพื่อดู นี่คือเหตุผลว่าทำไม "การบล็อก" จึงเป็นเรื่องใหญ่ - การเรียกใช้โค้ดที่เน้นการคำนวณจะช่วยป้องกันไม่ให้เบราว์เซอร์ผ่านขั้นตอน "เรียกใช้ JS" และเข้าสู่ขั้นตอน "แสดงผลหน้า" ซึ่งทำให้หน้าค้างหรือกระตุก

ไปป์ไลน์ของ Chrome มีลักษณะดังนี้:

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

อย่างที่คุณเห็น JavaScript ทั้งหมดเกิดขึ้นก่อน จากนั้นหน้าจะได้รับการออกแบบจัดวางทาสีและประกอบ - "การแสดงผล" ไม่ใช่ทั้งหมดของไปป์ไลน์นี้จะดำเนินการทุกเฟรม ขึ้นอยู่กับว่าองค์ประกอบของหน้าเปลี่ยนแปลงไปอย่างไรถ้ามีและจะต้องแสดงผลอย่างไร

หมายเหตุ: alert()ยังซิงโครนัสและดำเนินการในระหว่างขั้นตอน JavaScript ซึ่งเป็นสาเหตุที่กล่องโต้ตอบการแจ้งเตือนปรากฏขึ้นก่อนที่คุณจะเห็นการเปลี่ยนแปลงในหน้าเว็บ

ตอนนี้คุณอาจถามว่า "เดี๋ยวก่อนสิ่งที่รันในขั้นตอน 'JavaScript' ในท่อส่งโค้ดทั้งหมดของฉันทำงาน 60 ครั้งต่อวินาทีหรือไม่? คำตอบคือ "ไม่" และย้อนกลับไปที่การทำงานของลูปเหตุการณ์ JS โค้ด JS จะทำงานก็ต่อเมื่ออยู่ในสแต็กเท่านั้น - จากสิ่งต่างๆเช่นผู้ฟังเหตุการณ์การหมดเวลาหรืออะไรก็ตาม ดูวิดีโอก่อนหน้า (จริงๆ)

https://developers.google.com/web/fundamentals/performance/rendering/


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

@apieceofbart เบราว์เซอร์อื่น ๆ อาจทาสีหน้าใหม่แบบอะซิงโครนัสได้ในขณะที่หยุดสิ่งที่จาวาสคริปต์จนกว่าผู้ใช้จะจัดการกับหน้าต่างการแจ้งเตือน ไม่ได้หมายความว่าพวกเขาต้องรอให้การทาสีใหม่เกิดขึ้น
Jonas Schäfer

27

ใช่มันเป็นแบบซิงโครนัสเนื่องจากใช้งานได้ (ไปข้างหน้าพิมพ์ในคอนโซลของคุณ):

document.body.innerHTML = 'text';
alert(document.body.innerHTML);// you will see a 'text' alert

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


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

3
การแจ้งเตือนแสดงข้อความที่ถูกต้องหรือไม่ ( textในตัวอย่างของฉัน) ที่จะตอบคำถามของคุณว่ามันซิงโครนัสหรือไม่ การแสดงผลของเบราว์เซอร์เทียบกับการเรียกใช้ Javascript คือแอปเปิ้ลและส้ม :)
d -_- b

มี แต่ไม่เกี่ยวข้องกับคำถาม
apieceofbart

1
คำถามของคุณคือ "innerHTML asynchronous หรือไม่" หากสามารถใช้ค่านี้ได้ทันทีหลังจากซิงโครนัสไม่? ฉันคิดว่าคุณหมายถึงถามเพิ่มเติมเกี่ยวกับการแสดงผลหน้าไม่ใช่คุณภาพซิงโครนัสของ innerHTML
ง -_- ข

8
ใช่คำถามนั้นผิดอย่างชัดเจน แต่ฉันไม่รู้ว่าจะถามอะไร หากฉันได้รู้จักสิ่งที่ถูกต้องหากจะพบคำตอบ ฉันไม่ได้ตั้งใจที่จะใจร้ายขอบคุณสำหรับคำตอบอยู่แล้ว!
apieceofbart

6

innerHTMLทรัพย์สินที่เกิดขึ้นจริงไม่ได้รับการปรับปรุงพร้อมกัน แต่วาดภาพว่าการเปลี่ยนแปลงนี้เกิดขึ้นทำให้เกิดการถ่ายทอดสด

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

คุณสามารถดูสิ่งนี้ได้สองวิธี:

  1. หากคุณเพิ่มfor(var i=0; i<1000000; i++) { }ก่อนการแจ้งเตือนแสดงว่าคุณให้เวลาเบราว์เซอร์ในการวาดใหม่มากพอสมควร แต่ก็ไม่ได้เนื่องจากยังไม่ได้ล้างสแต็กฟังก์ชัน ( addยังทำงานอยู่)

  2. หากคุณหน่วงเวลาalertด้วยอะซิงโครนัสsetTimeout(function() { alert('random'); }, 1)กระบวนการวาดใหม่จะดำเนินการต่อฟังก์ชันล่าช้าโดย setTimeout

    • การดำเนินการนี้จะไม่ได้ผลหากคุณใช้การหมดเวลา0อาจเป็นเพราะ Chrome ให้ลำดับความสำคัญของคิวเหตุการณ์ในการ0หมดเวลาก่อนเหตุการณ์อื่น ๆ (หรืออย่างน้อยก่อนเหตุการณ์วาดใหม่)

ขอบคุณที่ตอบ! โปรดอย่าว่าแม้จะsetTimeout(func, 1)ไม่ได้ผลทุกครั้งตรวจสอบวิดีโอนี้: youtu.be/r8caVE_a5KQ
apieceofbart
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.