Το Layout thrashing (γνωστό και ως αναγκαστικός συγχρονισμένος επαναϋπολογισμός διάταξης ή forced reflow) συμβαίνει όταν η JavaScript εναλλάσσεται επανειλημμένα μεταξύ εγγραφών στο DOM και ανάγνωσης γεωμετρικών ιδιοτήτων διάταξης.
Αιτίες Ακύρωσης Διάταξης (Layout Invalidation)
Οι παρακάτω ιδιότητες και μέθοδοι DOM επιβάλλουν συγχρονισμένο επαναϋπολογισμό της γεωμετρίας κάθε φορά που η διάταξη έχει ακυρωθεί από προηγούμενες μεταβολές στο DOM.
Ιδιότητες & Μέθοδοι Στοιχείων
Γεωμετρία Πλαισίου & Διαστάσεις
clientHeight,clientWidthclientLeft,clientTopoffsetHeight,offsetWidthoffsetLeft,offsetTopoffsetParentscrollHeight,scrollWidthscrollLeft,scrollTop
Ερωτήματα Ορίων (Bounding Boxes)
getBoundingClientRect()getClientRects()
Ενέργειες Κύλισης (Scroll)
scrollIntoView()scrollIntoViewIfNeeded()scrollByLines()scrollByPages()
Κείμενο & Εστίαση
innerTextouterTextfocus()(επιβάλλει προσαρμογές δακτυλίου εστίασης και κύλισης)
Ιδιότητες Συμβάντων Ποντικιού
Η ανάγνωση συγκεκριμένων συντεταγμένων σε ένα αντικείμενο MouseEvent μπορεί να πυροδοτήσει υπολογισμό διάταξης:
layerX,layerYoffsetX,offsetY
Μέθοδοι & Ιδιότητες Window
window.getComputedStyle()(κατά τον έλεγχο τιμών που εξαρτώνται από τη διάταξη, όπως πλάτος, ύψος ή περιθώρια)window.scrollBy()window.scrollTo()window.scrollX,window.scrollY
Frame, Έγγραφο & Εικόνα
- Ανάγνωση
widthήheightσε εικόνα πριν σταθεροποιηθεί η διάταξη - Πρόσβαση στο
document.body.scrollHeight
Λειτουργίες jQuery
Πολλές βοηθητικές μέθοδοι του jQuery διαβάζουν εσωτερικά υπολογισμένες τιμές διάταξης:
// Πυροδοτεί reflow
$(elem).offset();
$(elem).offsetParent();
$(elem).position();
$(elem).scrollLeft();
$(elem).scrollTop();
$(elem).css('width'); // Οποιοσδήποτε υπολογισμός στυλ εξαρτώμενος από διάταξη
$(elem).text();
// Ψευδο-επιλογείς που ελέγχουν γεωμετρική ορατότητα
$(':hidden');
$(':visible');
$(':contains');
Βέλτιστες Πρακτικές Αποφυγής Layout Thrashing
1. Ομαδοποίηση Αναγνώσεων και Εγγραφών
Εκτελείτε πάντα όλες τις μετρήσεις DOM πρώτα και μετά ομαδοποιήστε όλες τις τροποποιήσεις DOM μαζί.
// ΚΑΚΟ: Εναλλαγή ανάγνωσης/εγγραφής προκαλεί επαναϋπολογισμό σε κάθε επανάληψη
elements.forEach((el) => {
const width = el.clientWidth; // ΑΝΑΓΝΩΣΗ (αναγκάζει layout)
el.style.width = width * 2 + 'px'; // ΕΓΓΡΑΦΗ (ακυρώνει layout)
});
// ΚΑΛΟ: Ομαδικές αναγνώσεις, έπειτα ομαδικές εγγραφές
const widths = elements.map((el) => el.clientWidth); // ΟΜΑΔΙΚΗ ΑΝΑΓΝΩΣΗ
elements.forEach((el, index) => {
el.style.width = widths[index] * 2 + 'px'; // ΟΜΑΔΙΚΗ ΕΓΓΡΑΦΗ
});
2. Χρήση του requestAnimationFrame
Προγραμματίστε αλλαγές στυλ στο επόμενο animation frame ώστε ο browser να βελτιστοποιήσει τον κύκλο απόδοσης:
const height = element.clientHeight; // Ανάγνωση στην τρέχουσα φάση
window.requestAnimationFrame(() => {
element.style.height = height + 50 + 'px'; // Εγγραφή στο επόμενο καρέ
});
3. Χρήση CSS Transforms & Opacity
Προτιμήστε κίνηση ιδιοτήτων CSS που διαχειρίζεται το νήμα σύνθεσης (transform, opacity) αντί ιδιοτήτων διάταξης (top, left, width, height).