Hướng dẫn tạo website miễn phí với Google Sites cho người mới
Google Sites là công cụ tạo website miễn phí của Google, cho phép bất kỳ ai có tài khoản Gmail đều có thể xây dựng một trang web chỉ bằng thao tác kéo thả mà không cần biết một dòng...
Trong suốt mấy năm làm nghề, mình nhận thấy một điều khá thú vị: hầu như ai khi làm web cũng muốn có những hiệu ứng hình ảnh thật lung linh để thu hút người xem. Tuy nhiên, ranh giới giữa một website “đẹp” và một website “nặng” đôi khi rất mong manh. Bản thân mình thời gian đầu cũng từng mắc sai lầm khi cài đặt quá nhiều thư viện mà không để ý rằng chúng đang “ngốn” tài nguyên của người dùng như thế nào.
Hôm nay, mình muốn chia sẻ với bạn về cách mình đang dùng thư viện Fancybox. Đây là một công cụ mình thấy rất ổn định để hiển thị ảnh và video. Thay vì chỉ hướng dẫn cài đặt thông thường, mình sẽ chia sẻ cách mình tối ưu nó để trang web vẫn chạy thanh thoát, đạt điểm số tốt trên các công cụ đo lường và quan trọng nhất là khách hàng của bạn cảm thấy thoải mái khi lướt web.

Nếu bạn chưa biết thì Fancybox là một thư viện JavaScript giúp tạo ra các hộp thoại (lightbox) để phóng to hình ảnh, xem video hoặc các nội dung khác ngay trên trang hiện tại.
Trước đây, Fancybox phụ thuộc khá nhiều vào jQuery, nhưng ở các phiên bản mới (như v5), nó đã chuyển sang dùng thuần JavaScript (Vanilla JS). Điều này đối với mình là một điểm cộng lớn vì nó giúp giảm bớt sự phụ thuộc vào các thư viện cũ, giúp web gọn nhẹ hơn.
Mình luôn tâm niệm rằng, một website tốt trước hết phải là một website nhanh. Khách hàng bây giờ khá thiếu kiên nhẫn; chỉ cần trang xoay vòng quá 3 giây là họ có thể rời đi ngay.
Khi mình kiểm tra nhiều website, mình thấy lỗi phổ biến là mọi người nạp tất cả các thư viện ngay từ khi trang bắt đầu tải (trong thẻ <head>). Điều này khiến trình duyệt bị “ngợp”. Thực tế, người dùng chưa chắc đã click vào xem ảnh ngay khi vừa vào trang, vậy tại sao chúng ta phải bắt họ tải đống code đó ngay lập tức?
Bằng cách áp dụng kỹ thuật load “lười” (lazy load) và chỉ nạp khi cần, mình đã giúp nhiều dự án cải thiện đáng kể chỉ số Core Web Vitals. Bạn có thể xem thêm về cách mình tối ưu và tăng tốc độ website WordPress để hiểu thêm về tầm quan trọng của việc tiết kiệm từng mili giây tải trang.
Mình sẽ hướng dẫn bạn cách làm sao để tích hợp Fancybox vào website một cách hiệu quả, tối ưu nhất.
Thay vì chỉ dùng thẻ ảnh thông thường, mình bao bọc nó trong một thẻ link với các thuộc tính data. Việc này giúp script nhận diện được ảnh nào thuộc nhóm nào.
<a data-fancybox="gallery-portfolio"
data-src="https://domain.com/anh-full-1.jpg"
data-thumb="https://domain.com/anh-thumb-1.jpg"
href="#">
<img src="https://domain.com/anh-thumb-1.jpg"
alt="Mô tả SEO ảnh 1"
width="600" height="400" loading="lazy">
</a>
<button data-open-gallery="gallery-portfolio"
data-start-index="2">
Xem toàn bộ 12 ảnh dự án
</button>
Lưu ý từ Phúc: Luôn nhớ thêm thuộc tính width, height và loading=”lazy” cho ảnh thumbnail để tránh lỗi CLS (thay đổi bố cục đột ngột).
Đây là phần không thể thiếu trong bài viết này của mình. Thay vì load Fancybox ngay khi tải trang, mình sẽ chỉ load khi người dùng di chuột gần ảnh hoặc click vào ảnh.
/* ===============================
FANCYBOX GALLERY WITH THUMBS - TỐI ƯU BỞI LÊ PHÚC
================================ */
let fancyboxPromise = null;
let isOpening = false;
/* ---------- LOAD LAZY: Chỉ tải file khi cần ---------- */
function preloadFancybox() {
if (window.Fancybox) return Promise.resolve();
if (fancyboxPromise) return fancyboxPromise;
fancyboxPromise = new Promise(resolve => {
const css = document.createElement("link");
css.rel = "stylesheet";
css.href = "https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css";
const js = document.createElement("script");
js.src = "https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js";
js.defer = true;
js.onload = () => requestAnimationFrame(resolve);
document.head.append(css, js);
});
return fancyboxPromise;
}
/* ---------- BUILD ITEMS: Quét ảnh trong group ---------- */
function buildGalleryItems(group) {
return Array.from(
document.querySelectorAll(`[data-fancybox="${group}"]`)
).map(el => ({
src: el.dataset.src,
type: "image",
thumb: el.dataset.thumb || el.dataset.src
}));
}
/* ---------- OPEN GALLERY: Mở với hiệu ứng mượt ---------- */
function openGallery(group, startIndex = 0) {
if (!window.Fancybox || isOpening) return;
if (Fancybox.getInstance()) return;
const items = buildGalleryItems(group);
if (!items.length) return;
isOpening = true;
requestAnimationFrame(() => {
Fancybox.show(items, {
group,
startIndex,
animationEffect: "fade",
animated: true,
dragToClose: true,
trapFocus: true,
autoFocus: true,
Hash: true, // Hỗ trợ link URL chia sẻ
preload: 1, // Chỉ load trước 1 ảnh kế tiếp để tiết kiệm băng thông
lazyLoad: true,
Thumbs: {
autoStart: items.length > 1,
type: "classic"
},
Image: {
zoom: true,
click: "toggleZoom",
wheel: "zoom"
},
on: {
ready: (fancyboxRef) => {
const newIndex = startIndex || fancyboxRef.getSlide().index;
history.replaceState(null, "", `#${group}-${newIndex + 1}`);
},
"Carousel.change": (fancyboxRef) => {
const newIndex = fancyboxRef.getSlide().index + 1;
history.replaceState(null, "", `#${group}-${newIndex}`);
},
destroy: () => { isOpening = false; }
}
});
});
}
/* ---------- HASH SUPPORT: Tự động mở từ link chia sẻ ---------- */
function parseHash() {
const hash = window.location.hash.replace("#", "");
if (!hash) return null;
const match = hash.match(/^(.+)-(\d+)$/);
if (!match) return null;
return {
group: match[1],
index: parseInt(match[2], 10) - 1
};
}
async function openFromHash() {
const data = parseHash();
if (!data) return;
const exists = document.querySelector(`[data-fancybox="${data.group}"]`);
if (!exists) return;
await preloadFancybox();
openGallery(data.group, data.index);
}
window.addEventListener("load", openFromHash);
/* ---------- CLICK EVENT: Lắng nghe click ---------- */
document.addEventListener("click", async e => {
const trigger = e.target.closest("[data-open-gallery], [data-fancybox]");
if (!trigger) return;
e.preventDefault();
await preloadFancybox();
let group = trigger.dataset.openGallery || trigger.dataset.fancybox;
const startIndex = trigger.dataset.openGallery
? parseInt(trigger.dataset.startIndex || 0)
: 0;
openGallery(group, startIndex);
});
Ngoài ra nếu bạn muốn tìm hiểu sâu hơn về các config và thuộc tính của Fancybox có thể tham khảo thêm tại https://fancyapps.com/fancybox/
Nếu bạn đã đọc bài viết về cách trì hoãn GTM của mình, bạn sẽ thấy mình rất ưa chuộng phương pháp “Lazy Loading Assets”.
Qua hàng chục dự án, mình rút ra được vài mẹo nhỏ bạn hãy thử tham khảo xem nha :>
openGallery. Điều này giúp giảm số lượng DOM node, tốt cho điểm PageSpeed.Sau khi áp dụng bộ code chuẩn này cho các dự án gần đây, mình ghi nhận được những con số rất tích cực:

Việc sử dụng thư viện Fancybox không khó, nhưng dùng sao cho chuẩn SEO và tối ưu hiệu suất thì đòi hỏi một chút sự tận tâm trong cách xử lý code. Hy vọng với những chia sẻ thực tế và bộ code mình đã tinh chỉnh, bạn có thể tự tin triển khai cho website của mình hoặc khách hàng.
Nếu bạn gặp khó khăn trong quá trình cài đặt hoặc muốn tối ưu sâu hơn cho WordPress, đừng ngần ngại để lại comment hoặc liên hệ với mình. Chúc bạn có một website vừa đẹp, vừa nhanh!