- vừa được xem lúc

Hướng dẫn tạo ứng dụng cuộn ngang bằng HTML, CSS và JavaScript

0 0 5

Người đăng: Hoàng Quang

Theo Viblo Asia

Chào mọi người,

Đầu năm đi làm lại. Chúc mọi người một năm mới nhiều sức khoẻ, vạn sự như ý!

Hôm nay, mình xin chia sẻ lại một tiện ích (widget) rất cool để sử dụng cho các web/blog cá nhân hay doanh nghiệp đều cũng sẽ thấy rất ổn và nhìn vào rất chuyên nghiệp. Nào, chúng ta cùng bắt tay vào làm việc thôi!

Tạo các phần tử HTML

Trong đoạn mã HTML này, chúng ta có một phần tử <div> có class là "container" chứa toàn bộ nội dung. Bên trong đó, chúng ta có một phần tử <div> khác có class là "top-bar" chứa các thông tin về các sự kiện sắp diễn ra. Trong phần này, chúng ta thấy một tiêu đề gọi là "Upcoming events", và hai nút button đại diện cho việc cuộn ngang để xem các sự kiện trước hoặc sau.

Dưới "top-bar", chúng ta có một phần tử <div> khác có id là "events". Trong phần này, chúng ta có một danh sách các sự kiện (được biểu diễn dưới dạng các phần tử <a>). Mỗi sự kiện bao gồm một hình ảnh, ngày diễn ra sự kiện và loại sự kiện, cũng như một mô tả vắn tắt về sự kiện đó.

Mỗi sự kiện cũng có một số thông tin khác như số bài viết mới và số vé đã được mua được hiển thị bên dưới mô tả sự kiện.

Cuối cùng, cấu trúc của mỗi sự kiện bao gồm hình ảnh đại diện, ngày và tháng diễn ra sự kiện, loại sự kiện và mô tả sự kiện, tạo nên một giao diện thú vị để hiển thị thông tin về các sự kiện sắp diễn ra.

<div class="container"> <div class="top-bar"> <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12z" /> <path d="M16 3v4" /> <path d="M8 3v4" /> <path d="M4 11h16" /> <path d="M7 14h.013" /> <path d="M10.01 14h.005" /> <path d="M13.01 14h.005" /> <path d="M16.015 14h.005" /> <path d="M13.015 17h.005" /> <path d="M7.01 17h.005" /> <path d="M10.01 17h.005" /> </svg> <h2> Upcoming events </h2> <button type="button" disabled id="action-button--previous" class="action-button--horizontal-scroll"> <svg width="16" height="16" fill="currentColor" focusable="false" viewBox="0 0 24 24"> <path d="M12.771 7.115a.829.829 0 0 0-1.2 0L3 15.686l1.2 1.2 7.971-7.971 7.972 7.971 1.2-1.2-8.572-8.571Z"> </path> </svg> </button> <button type="button" id="action-button--next" class="action-button--horizontal-scroll"> <svg width="16" height="16" fill="currentColor" focusable="false" viewBox="0 0 24 24"> <path d="M12.771 7.115a.829.829 0 0 0-1.2 0L3 15.686l1.2 1.2 7.971-7.971 7.972 7.971 1.2-1.2-8.572-8.571Z"> </path> </svg> </button> </div> <div id="events"> <a href="#the-weeknd" class="event"> <div class="event__image"> <img src="https://www.urbanstage.cz/wp-content/uploads/2020/03/weeknd.jpg" alt="The Weeknd"> <div class="event__indicator event__date"> 08 <div class="event__date__month"> Feb </div> </div> <div class="event__indicator event__type"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 5l0 2" /> <path d="M15 11l0 2" /> <path d="M15 17l0 2" /> <path d="M5 5h14a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-3a2 2 0 0 0 0 -4v-3a2 2 0 0 1 2 -2" /> </svg> </div> </div> <div class="event-description"> <h2> The Weeknd - After Hours Tour </h2> <div class="bottom-stats"> <div class="bottom-stat"> <div class="circle circle--red"></div> 2 new posts </div> <div class="bottom-stat"> <div class="circle circle--green"></div> 5 tickets </div> </div> </div> </a> <a href="#the-weeknd" class="event"> <div class="event__image"> <img src="https://media.architecturaldigest.com/photos/641b2b8252ae61ead67e92d9/16:9/w_2560%2Cc_limit/GettyImages-1474485122.jpg" alt="The Weeknd"> <div class="event__indicator event__date"> 16 <div class="event__date__month"> Feb </div> </div> <div class="event__indicator event__type"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 5l0 2" /> <path d="M15 11l0 2" /> <path d="M15 17l0 2" /> <path d="M5 5h14a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-3a2 2 0 0 0 0 -4v-3a2 2 0 0 1 2 -2" /> </svg> </div> </div> <div class="event-description"> <h2> Taylor Swift - Eras Tour </h2> <div class="bottom-stats"> <div class="bottom-stat"> <div class="circle circle--red"></div> 4 new posts </div> <div class="bottom-stat"> <div class="circle circle--green"></div> 10 tickets </div> </div> </div> </a> <a href="#the-weeknd" class="event"> <div class="event__image"> <img src="https://fox2now.com/wp-content/uploads/sites/14/2021/09/GettyImages-451833751.jpg?w=1280" alt="The Weeknd"> <div class="event__indicator event__date"> 20 <div class="event__date__month"> Feb </div> </div> <div class="event__indicator event__type"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 5l0 2" /> <path d="M15 11l0 2" /> <path d="M15 17l0 2" /> <path d="M5 5h14a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-3a2 2 0 0 0 0 -4v-3a2 2 0 0 1 2 -2" /> </svg> </div> </div> <div class="event-description"> <h2> Imagine Dragons </h2> <div class="bottom-stats"> <div class="bottom-stat"> <div class="circle circle--red"></div> 2 new posts </div> <div class="bottom-stat"> <div class="circle circle--green"></div> 4 tickets purchased </div> </div> </div> </a> <a href="#the-weeknd" class="event"> <div class="event__image"> <img src="https://i.dailymail.co.uk/i/pix/2012/10/05/article-2212936-155E4783000005DC-817_1024x615_large.jpg" alt="The Weeknd"> <div class="event__indicator event__date"> 28 <div class="event__date__month"> March </div> </div> <div class="event__indicator event__type"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> <path stroke="none" d="M0 0h24v24H0z" fill="none" /> <path d="M15 5l0 2" /> <path d="M15 11l0 2" /> <path d="M15 17l0 2" /> <path d="M5 5h14a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-3a2 2 0 0 0 0 -4v-3a2 2 0 0 1 2 -2" /> </svg> </div> </div> <div class="event-description"> <h2> U2 </h2> <div class="bottom-stats"> <div class="bottom-stat"> <div class="circle circle--red"></div> 2 new posts </div> <div class="bottom-stat"> <div class="circle circle--green"></div> 3 tickets purchased </div> </div> </div> </a> </div> </div>

Thêm các lớp CSS

Dùng đoạn mã css bên dưới để định hình cho nội dung HTML trên được rõ ràng hơn.

.top-bar { display: flex; gap: 16px; align-items: center; width: 100%; flex-direction: row; color: var(--headline-color);
} .top-bar h2 { font-size: 1.2rem; font-weight: 700; line-height: 1.5rem;
} .top-bar svg { color: var(--headline-color);
} .action-buttons { position: relative; display: inline-flex; flex: 0 0 auto; gap: 8px; scroll-snap-align: start; width: 100%; flex-wrap: wrap;
} .action-button { color: #163300; border-radius: 16px; height: 32px; gap: 4px; cursor: pointer; display: flex; padding: 0 12px; font-weight: 600; transition: all 0.2s ease-in-out; white-space: nowrap; align-items: center; text-decoration: none; background: var(--secondary);
} .action-button--primary:active { background: var(--accent-active);
} .action-button:active { background: var(--secondary-active);
} .action-button--horizontal-scroll { border-radius: 50%; cursor: pointer; border: none; outline: none; width: 32px; height: 32px; background: var(--accent);
} .action-button--horizontal-scroll:hover { background: var(--accent-hover);
} .action-button--horizontal-scroll:active { background: var(--accent-active);
} .action-button--horizontal-scroll:disabled { cursor: not-allowed; background: var(--secondary);
} .action-button--horizontal-scroll:disabled:hover { background: var(--secondary);
} .action-button--horizontal-scroll:disabled:active { background: var(--secondary);
} #action-button--previous { margin-left: auto; transform: rotate(-90deg);
} #action-button--next { transform: rotate(90deg);
} .action-button:hover { background: var(--secondary-hover);
} .action-button--primary { background: var(--accent);
} .action-button--primary:hover { background: var(--accent-hover);
} .action-button--primary:active { background: var(--accent-active);
} #events { display: flex; gap: 1.5rem; padding: 1rem 0px; width: 100%; flex-direction: row; overflow: auto;
} .event { appearance: none; text-decoration: none; position: relative; transition: all 0.2s ease-in-out; min-width: 320px; cursor: pointer; flex-direction: column; justify-content: space-between; scroll-snap-align: start; overflow: hidden; color: rgba(29, 32, 59, 1);
} .event__image { position: relative;
} .event__indicator { position: absolute; padding: 0.5rem; min-width: 3.5rem; min-height: 3.5rem; display: flex; flex-direction: column; justify-content: center; background: var(--white); align-items: center; border-radius: 0.5rem;
} .event__date { top: 0.5rem; left: 0.5rem; font-size: 1.35rem;
} .event__date__month { font-size: 0.6rem; text-transform: uppercase; font-weight: bold; color: var(--event-headline);
} .event__type { top: 0.5rem; right: 0.5rem;
} .event__type svg { color: var(--event-text); height: 2rem; width: 2rem;
} .event-description { color: var(--event-text); padding: 0.5rem 0.75rem;
} .event img { width: 100%; height: 250px; border-radius: 1rem; object-fit: cover; object-position: top;
} .event h2 { font-size: 1.375rem; letter-spacing: normal; overflow-wrap: normal; white-space: nowrap; font-weight: 700; line-height: 1.25rem; color: var(--event-headline); overflow: hidden; text-overflow: ellipsis;
} .bottom-stats { display: flex; justify-content: space-between; gap: 1rem; width: 100%; flex-direction: row; color: var(--headline-color);
} .bottom-stat { display: flex; gap: 0.5rem; align-items: center; flex-direction: row;
} .container { max-width: 800px; width: 100%; min-width: 300px; margin: 120px auto;
} .circle { height: 0.5rem; width: 0.5rem; border-radius: 50%;
} .circle--green { background: var(--success);
} .circle--red { background: var(--error);
}

Thêm Javascript

const events = document.getElementById("events"); let isDown = false;
let startX;
let startY;
let scrollLeft;
let scrollTop; events.addEventListener("mousedown", (e) => { isDown = true; startX = e.pageX - events.offsetLeft; startY = e.pageY - events.offsetTop; scrollLeft = events.scrollLeft; scrollTop = events.scrollTop; events.style.cursor = "grabbing";
}); events.addEventListener("mouseleave", () => { isDown = false; events.style.cursor = "grab";
}); events.addEventListener("mouseup", () => { isDown = false; events.style.cursor = "grab";
}); document.addEventListener("mousemove", (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - events.offsetLeft; const y = e.pageY - events.offsetTop; const walkX = (x - startX) * 1; const walkY = (y - startY) * 1; events.scrollLeft = scrollLeft - walkX; events.scrollTop = scrollTop - walkY;
}); const scrollLeftButton = document.getElementById("action-button--previous");
const scrollRightButton = document.getElementById("action-button--next"); scrollLeftButton.addEventListener("click", () => { events.scrollBy({ top: 0, left: -200, behavior: "smooth", });
}); scrollRightButton.addEventListener("click", () => { events.scrollBy({ top: 0, left: 200, behavior: "smooth", });
}); events.addEventListener("scroll", (e) => { const position = events.scrollLeft; if (position === 0) { scrollLeftButton.disabled = true; } else { scrollLeftButton.disabled = false; } if (Math.round(position) === events.scrollWidth - events.clientWidth) { scrollRightButton.disabled = true; } else { scrollRightButton.disabled = false; }
}); 

Mã JavaScript trên là một đoạn mã điều khiển sự kiện scroll và drag trên một phần tử HTML mang ID "events".

Đầu tiên, mã này áp dụng một số biến để lưu trữ thông tin về trạng thái hiện tại của sự kiện kéo thả (mousedown), bao gồm vị trí chuột khi bắt đầu kéo thả (startX và startY) và vị trí scroll khi bắt đầu kéo thả (scrollLeftscrollTop).

Sau đó, nó gán các event listener để theo dõi hành vi của người dùng. Khi người dùng nhấn chuột vào phần tử "events" (mousedown), chương trình sẽ lưu trữ vị trí ban đầu của chuột và vị trí scroll. Khi rời khỏi phần tử "events" (mouseleave), hoặc thả chuột ra (mouseup), trạng thái của sự kiện kéo thả được cập nhật.

Khi di chuyển chuột trên tài liệu (mousemove) và nếu trạng thái kéo thả là đúng (isDown = true), chương trình sẽ tính toán sự thay đổi vị trí chuột và cập nhật vị trí scroll của phần tử "events" tương ứng.

Ngoài ra, mã cũng xử lý sự kiện khi người dùng click vào các nút scroll trái (scrollLeftButton) và scroll phải (scrollRightButton). Khi phần tử "events" được di chuyển, trạng thái của các nút scroll cũng được cập nhật để có thể bật/tắt chức năng scroll tương ứng.

Sau khi thêm đầy đủ các thành phần thì khi chạy thử trang web để xem, chúng ta sẽ có được kết quả như sau:

image.png

Đến đây là bạn đã code hoàn thành xong một tiện ích để sử dụng cho một trang web. Chúc các bạn thành công!

Bình luận

Bài viết tương tự

- vừa được xem lúc

Một số quy tắc và name class phổ biến cho Front-end Dev

Trong CSS, các thủ tục setting đóng vai trò to lớn, nhưng bên cạnh đó, cách đặt tên theo các từ đơn sao cho phù hợp cũng quan trọng không kém . Đặt tên rất khó, nhưng cũng rất quan trọng .

0 0 79

- vừa được xem lúc

2021, chúng ta cần tối ưu hóa việc tải hình ảnh trên web như nào?

Rất chào các bạn,. Như các bạn đã biết, trong kỉ nguyên công nghệ, song song với sự sinh ra dày đặc của các trang web mới cũng là sự biến mất của những trang web "lạc hậu" hay hoạt động kém hiệu quả.

0 0 43

- vừa được xem lúc

Tài nguyên nghiên cứu sâu Html

1. Articles and standards. . HTML 5.

0 0 183

- vừa được xem lúc

Tìm hiểu về Event.preventDefault(), Event.stopPropagation() và Event.stopImmediatePropagation()

Chúng ta thương thấy 3 method này và có thể dẫn dến bối rối và nhầm lẫn giữa chúng:. . Event.preventDefault().

0 0 36

- vừa được xem lúc

Vanilla JS Project: Tính tuổi

1. Yêu cầu.

0 0 50

- vừa được xem lúc

Thủ thuật nhỏ để căn chỉnh image với object-fit

Chào các bạn,. Có lẽ trong hành trình code của các bạn thì không ít lần gặp vấn đề méo ảnh do fix cứng cả width, height của ảnh nhỉ? Hoặc kể cả khi bạn set value cho 1 thuộc tính weigth hoặc height còn thuộc tính còn lại để auto thì nhiều lúc ảnh cũng không được hiển thị toàn vẹn cho lắm.

0 0 34