Hàm displaySlide là "trái tim" của trang học (subject.html). Mỗi khi người dùng nhấp vào một slide trong mục lục, hàm này được gọi với slideId tương ứng. Nhiệm vụ của nó là tìm nạp dữ liệu liên quan và cập nhật toàn bộ giao diện, bao gồm cả việc điều khiển bố cục hai cột một cách linh hoạt.
// **NÂNG CẤP LẦN CUỐI: Hàm hiển thị slide tích hợp nút Trước/Sau**
function displaySlide(slideId) {
// --- Lấy các element nút bấm ---
const prevBtn = document.getElementById('prev-slide-btn');
const nextBtn = document.getElementById('next-slide-btn');
// --- Phần 1: Tìm dữ liệu slide, section và index hiện tại ---
let currentSlideData = null;
let currentSection = null;
let currentSlideIndex = -1;
let allSlidesInSection = [];
for (const section of courseSections) {
const foundIndex = section.slides.findIndex(s => s.id === slideId);
if (foundIndex !== -1) {
currentSection = section;
currentSlideIndex = foundIndex;
currentSlideData = section.slides[foundIndex];
allSlidesInSection = section.slides;
break;
}
}
if (!currentSlideData) { return; }
// --- Phần 2: Hiển thị nội dung chính (giữ nguyên) ---
slideTitleEl.textContent = currentSlideData.title;
slideImageEl.src = currentSlideData.image;
slideNotesEl.innerHTML = currentSlideData.notes;
// --- Phần 3: Xử lý bố cục 2 cột (giữ nguyên) ---
const notesSection = document.querySelector('.notes-section');
const termsColumn = document.getElementById('terms-column');
const termsListEl = document.getElementById('slide-terms-content');
termsListEl.innerHTML = '';
const hasTerms = currentSlideData.terms && Object.keys(currentSlideData.terms).length > 0;
if (hasTerms) {
termsColumn.style.display = 'block';
notesSection.classList.remove('single-column');
for (const term in currentSlideData.terms) {
const dt = document.createElement('dt'); dt.textContent = term;
const dd = document.createElement('dd'); dd.textContent = currentSlideData.terms[term];
termsListEl.appendChild(dt); termsListEl.appendChild(dd);
}
} else {
termsColumn.style.display = 'none';
notesSection.classList.add('single-column');
}
// --- Phần 4: Logic cho nút Trước/Sau ---
// Nút "Trang trước"
if (currentSlideIndex > 0) {
prevBtn.disabled = false;
const prevSlideId = allSlidesInSection[currentSlideIndex - 1].id;
prevBtn.onclick = () => displaySlide(prevSlideId);
} else {
prevBtn.disabled = true; // Vô hiệu hóa nếu là slide đầu tiên
prevBtn.onclick = null;
}
// Nút "Trang sau"
if (currentSlideIndex < allSlidesInSection.length - 1) {
nextBtn.disabled = false;
const nextSlideId = allSlidesInSection[currentSlideIndex + 1].id;
nextBtn.onclick = () => displaySlide(nextSlideId);
} else {
nextBtn.disabled = true; // Vô hiệu hóa nếu là slide cuối cùng
nextBtn.onclick = null;
}
// --- Phần 5: Cập nhật mục lục (giữ nguyên) ---
document.querySelectorAll('#slide-navigation-container li').forEach(li => li.classList.remove('active'));
const activeLi = document.querySelector(`#slide-navigation-container li[data-id='${slideId}']`);
if (activeLi) { activeLi.classList.add('active'); }
}Thuật toán của hàm có thể được chia thành 4 bước chính, với sự thay đổi lớn nhất ở Bước 3.
Bước này không thay đổi. Mục tiêu là lấy ra dữ liệu của slide được yêu cầu.
- Khởi tạo: Tạo biến
currentSlideDatarỗng. - Vòng lặp: Lặp qua từng
section(tuần học), và dùng.find()để tìm slide cóidkhớp vớislideId. - Lưu kết quả & Thoát sớm: Nếu tìm thấy, gán dữ liệu vào
currentSlideDatavà dùngbreakđể kết thúc tìm kiếm. - Kiểm tra lỗi: Nếu không tìm thấy, kết thúc hàm.
Bước này không thay đổi. Cập nhật các thành phần chính trên giao diện.
- Cập nhật tiêu đề: Gán
currentSlideData.titlevàotextContentcủa tiêu đề. - Cập nhật ảnh: Gán
currentSlideData.imagevào thuộc tínhsrccủa ảnh. - Cập nhật ghi chú: Gán
currentSlideData.notesvàoinnerHTMLcủa vùng ghi chú.
Đây là phần logic được thiết kế lại hoàn toàn. Nó quyết định xem giao diện sẽ hiển thị 1 hay 2 cột.
- Tham chiếu đến các Element: Lấy ra các element HTML quan trọng:
notesSection: Vùng chứa chính (<section>) của cả hai cột.termsColumn: Cột thuật ngữ bên phải.termsListEl: Danh sách<dl>để chứa nội dung thuật ngữ.
- Dọn dẹp: Luôn xóa sạch nội dung của danh sách thuật ngữ cũ (
termsListEl.innerHTML = '') để chuẩn bị cho dữ liệu mới. - Ra quyết định: Kiểm tra xem slide hiện tại có chứa thuật ngữ hay không.
- Một biến boolean
hasTermsđược tạo ra. - Điều kiện kiểm tra là:
currentSlideData.termscó tồn tại VÀ số lượng "key" trong nó có lớn hơn 0 hay không (Object.keys(currentSlideData.terms).length > 0).
- Một biến boolean
- Phân luồng xử lý bố cục:
- Nếu
hasTermslàtrue(CÓ thuật ngữ): a. Hiện cột phải: Thiết lậptermsColumn.style.display = 'block'. b. Kích hoạt layout 2 cột: Xóa classsingle-columnkhỏinotesSection. CSS sẽ tự động áp dụngdisplay: flexvà chia đôi không gian. c. Điền dữ liệu: Lặp qua đối tượngcurrentSlideData.termsvà tạo các thẻ<dt>,<dd>để hiển thị nội dung thuật ngữ. - Nếu
hasTermslàfalse(KHÔNG có thuật ngữ): a. Ẩn cột phải: Thiết lậptermsColumn.style.display = 'none'. b. Kích hoạt layout 1 cột: Thêm classsingle-columnvàonotesSection. CSS sẽ làm cho cột ghi chú bên trái chiếm 100% chiều rộng.
- Nếu
Bước này không thay đổi. Phản hồi lại cho người dùng biết họ đang ở slide nào.
- Xóa highlight cũ: Xóa class
activekhỏi tất cả các mục<li>trong mục lục. - Thêm highlight mới: Tìm đúng mục
<li>códata-idkhớp vớislideIdvà thêm classactivevào cho nó.
Giúp người dùng chuyển đổi slide linh hoạt hơn
[ Bắt đầu ]
|
v
( Nhận slideId )
|
v
[ Tìm dữ liệu slide tương ứng ]
|
v
< Tìm thấy? > --Không--► [ Kết thúc ]
|
Có
|
v
[ Hiển thị title, ảnh, ghi chú ]
|
v
[ Xóa nội dung thuật ngữ cũ ]
|
v
< Slide hiện tại có thuật ngữ? > --Không--► [ Ẩn cột thuật ngữ & Áp dụng layout 1 cột ]
| |
Có |
| |
v |
[ Hiện cột thuật ngữ & Áp dụng layout 2 cột ] |
| |
v |
[ Điền dữ liệu thuật ngữ vào cột phải ] |
| |
+-------------------------------------------------+
|
v
[ Cập nhật mục lục: highlight slide đang active ]
|
v
[ Kết thúc ]