Triển khai Chế độ đọc tập trung cho bài viết WordPress mà không gây nhảy giao diện (CLS)

Focus Mode (Chế độ đọc tập trung) là một tính năng rất hữu ích cho các bài viết dài và tài liệu kỹ thuật. Tuy nhiên, nếu triển khai không đúng cách, việc ẩn sidebar sau khi trang đã render sẽ gây ra hiện tượng nhảy giao diện, làm tăng chỉ số Cumulative Layout Shift (CLS).

Triển khai Chế độ đọc tập trung cho bài viết WordPress mà không gây nhảy giao diện (CLS)

Bài viết này trình bày một cách triển khai Focus Mode thực tế, dựa trực tiếp trên code, với mấu chốt nằm ở việc sử dụng hook wp_head để quyết định layout ngay từ đầu.

Vấn đề thường gặp khi triển khai Focus Mode

Hầu hết các Focus Mode được xây dựng bằng cách chờ DOMContentLoaded rồi mới ẩn sidebar. Cách làm này khiến sidebar bị render trước, sau đó biến mất, dẫn đến việc cột nội dung bị dồn lại và gây nhảy giao diện.

Để tránh CLS, sidebar phải được ẩn trước lần paint đầu tiên của trình duyệt. Trong WordPress, điều này chỉ có thể thực hiện một cách an toàn thông qua hook wp_head.

Khởi tạo trạng thái Focus Mode thật sớm bằng wp_head

Đoạn code dưới đây được chạy rất sớm trong wp_head. Nó chỉ có nhiệm vụ kiểm tra trạng thái Focus Mode đã được lưu trong localStorage hay chưa, và nếu có thì gắn một class tạm thời lên thẻ html.

// ===== Focus Mode: singular only, chạy sớm =====
function init_html_early_focus_mode_inline_script() {
    if ( ! is_singular() ) {
        return;
    }
?>
<script>
(function () {
    try {
        if (localStorage.getItem('init_focus_mode') === '1') {
            document.documentElement.classList.add('init-focus-pending');
        }
    } catch (e) {}
})();
</script>
<?php
}
add_action('wp_head', 'init_html_early_focus_mode_inline_script', 1);

Điểm quan trọng ở đây là đoạn script này không thao tác với body, không query DOM, và không gây thay đổi layout. Nó chỉ đánh dấu trạng thái sớm để CSS có thể phản ứng ngay lập tức.

CSS render sớm để chống CLS

JavaScript chỉ đánh dấu trạng thái là chưa đủ. Sidebar chỉ thực sự không bị render khi CSS tương ứng đã tồn tại trước lần paint đầu tiên. Vì vậy, CSS chống CLS cũng phải được inline trong wp_head.

// Focus mode EARLY anti-CLS
add_action('wp_head', function () {
    if ( ! is_singular() ) {
        return;
    }
?>
<style>
html.init-focus-pending body.single-post #id-sidebar {
    display: none !important;
}
html.init-focus-pending body.single-post #id-content {
    margin-inline: auto;
}
</style>
<?php
}, 1);

Với đoạn CSS này, nếu Focus Mode đang bật, sidebar sẽ không bao giờ được render ngay từ đầu. Kết quả là layout ổn định tuyệt đối và không phát sinh CLS.

CSS cho trạng thái Focus Mode sau khi trang đã tải

Sau khi trang đã render xong, Focus Mode sẽ được điều khiển bằng class gắn vào body. CSS lúc này không còn ảnh hưởng đến layout ban đầu nữa.

/* Focus Mode */

body.is-focus-mode #id-sidebar {
    display: none !important;
}

body.is-focus-mode #id-content {
    margin-inline: auto;
}

Đồng bộ trạng thái và render nút bật tắt

Khi DOMContentLoaded, trạng thái tạm thời trên html sẽ được đồng bộ sang body. Đồng thời, nút bật tắt Focus Mode được render để người dùng có thể tương tác.

// FOCUS
document.addEventListener('DOMContentLoaded', () => {
    if (!document.body.classList.contains('single-post')) {
        return;
    }

    const STORAGE_KEY = 'init_focus_mode';
    const root = document.documentElement;

    let isEnabled = root.classList.contains('init-focus-pending');

    // Sync early state → body
    if (isEnabled) {
        document.body.classList.add('is-focus-mode');
        root.classList.remove('init-focus-pending');
    }

    const btn = document.createElement('button');
    btn.className = [
        'uk-button',
        'uk-button-secondary',
        'uk-border-pill',
        'uk-position-fixed',
        'uk-position-small',
        'uk-position-bottom-right',
        'uk-box-shadow-medium',
        'uk-visible@l',
        'init-focus-toggle'
    ].join(' ');

    btn.style.zIndex = 9999;
    document.body.appendChild(btn);

    const render = (enabled) => {
        btn.innerHTML = enabled
            ? `<span uk-icon="icon: close"></span> ${initHTMLData.exit || 'Exit'}`
            : `<span uk-icon="icon: file-text"></span> ${initHTMLData.focus || 'Focus'}`;
    };

    render(isEnabled);

    btn.addEventListener('click', () => {
        isEnabled = !isEnabled;

        document.body.classList.toggle('is-focus-mode', isEnabled);
        localStorage.setItem(STORAGE_KEY, isEnabled ? '1' : '0');
        render(isEnabled);
    });
});

Từ thời điểm này trở đi, việc bật hoặc tắt Focus Mode chỉ là thay đổi class trên body, không còn ảnh hưởng đến layout ban đầu và không gây CLS.

Kết luận

Mấu chốt của Focus Mode không nằm ở UI hay nút bấm, mà nằm ở việc quyết định layout đủ sớm. Bằng cách sử dụng wp_head để khởi tạo trạng thái và CSS chống CLS trước khi trình duyệt paint, Focus Mode có thể hoạt động mượt mà, ổn định và không làm ảnh hưởng đến Core Web Vitals.

Cách triển khai này đặc biệt phù hợp cho blog kỹ thuật và hệ thống tài liệu, nơi trải nghiệm đọc và hiệu năng luôn là ưu tiên hàng đầu.

Bình luận


1 bình luận
  • Admin

    21/01/2026 lúc 22:46

    nút Tập Trung góc dưới bên phải trang này chính là ví dụ cho việc nhảy giao diện (CLS) 😂😂😂
    vào chế độ tập trung (PC) và F5 để biết thứ bài viết này tránh!!!

Công cụ trực tuyến

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...