01/10/2018, 10:56

Scroll để load thay vì phân trang

Em đang học PHP(với MySQL) và có sử dụng AJAX(với jQuery) để không bị load lại trang.
Trong quá trình làm, em có nảy sinh một vấn đề như thế này, em đặc biệt hóa cho dễ hình dung:
Coi như đang làm một forum. Trang chính của forum này sẽ liệt kê tất cả các bài đăng, theo thứ tự cái nào hoạt động gần đây nhất thì ở trên, cái nào hoạt động sau thì ở dưới. Forum không làm phân trang. Ban đầu chỉ hiển thị 10 bài đăng(chẳng hạn như thế), khi cuộn xuống đáy trang thì AJAX sẽ giúp trả về tiếp 10 bài đăng tiếp theo - tương tự như diễn đàn


Về cách thức, em đã google để biết cách bắt sự kiện chạm đáy trang như thế nào.

$(document).ready(function(){
    $(window).scroll(function(){
        if($(window).scrollTop()==$(document).height()-$(window).height()){
             // ajax
        }
    });
});

Đến đây, việc còn lại là xử lý tại PHP và trả về kết quả dạng HTML và trình bày ra trình duyệt.
Em nghĩ ra 2 cách cho việc này và cả hai cách đều có chung ý tưởng là dùng mệnh đề LIMIT trong MySQL.
Cách 1. Tất cả các bài đăng sẽ được đặt trong một thẻ nào đó, chẳng hạn thẻ div với idnewfeed chẳng hạn.
Ban đầu, khi mới vào trang, trang chỉ load và hiển thị 10 bài đăng nhờ câu lệnh

SELECT * FROM posts ORDER BY lastActivity LIMIT 10

(em gõ SELECT * cho ngắn)
Trong đó, posts là bảng chứa các bài đăng, lastActivity là cột của bảng đó.
Mỗi bài đăng được đặt trong một thẻ tr chẳng hạn, em sẽ gắn cho nó thuộc tính class một giá trị là post. Với AJAX, em sẽ gửi lên file PHP số bài đăng đang xuất hiện ở trình duyệt bằng cách dùng DOM, cụ thể là $(".post").length
Khi đã được gửi tới file PHP, cái biến vừa gửi chứa giá trị là số bài đăng đang được hiển thị(em đặt là $cur_posts chẳng hạn) và áp dụng mệnh đề LIMIT:

SELECT * FROM posts ORDER BY lastActivity LIMIT $cur_posts+10

Điều này được hiểu là ban đầu load từ 0 đến 9, nhưng lần này lại load từ 0 đến 19, và sau đó em dùng vòng lặp foreach để lưu vào biến, echo nó ra để trả về cho AJAX, AJAX lại trả về cho thẻ div chứa toàn bộ bài đăng và ta lại có thêm 10 bài nữa. Cứ như vậy, mỗi khi chạm đáy trang, trang lại tải thêm 10 bài mới.


Cách 2. Với cách này, em cũng gửi lên số trang đang được hiển thị, nhưng mệnh đề LIMIT được viết khác:

SELECT * FROM posts ORDER BY lastActivity LIMIT $cur_posts,10

Cách này được hiểu là ban đầu load từ 0 đến 9, sau đó lại load thêm từ 10 đến 19,… cứ như vậy.
Và để trả về cho HTML thì em sử dụng phương thức append() của jQuery.


Vấn đề thực sự. Em chỉ nghĩ ra được 2 cách đó.
Với cách thứ nhất thì lần 1 tải 10, lần 2 tải 20, càng scroll nhiều càng load nhiều và càng chậm. Em rất coi trọng hiệu suất nên không chọn cách 1.
Với cách thứ hai thì mỗi lần đều đặn tải 10, em thấy khá ổn, thế nhưng nghĩ kĩ thì nó lại có vấn đề không ổn chút nào: Trong tình huống đang xem bài, chẳng hạn 10 bài là a, b, c, d, e, f, g, h,i, j thì em lại mở một tab mới và tạo hẳn một bài đăng mới là bài đăng A. Rồi em quay lại forum scroll tiếp. Theo mong đợi, khi em chạm đáy trang thì nó phải load tiếp từ k, l, m, n, o, p,… Thế nhưng cơ sở dữ liệu đã thay đổi vì lúc nãy bị thêm bài đăng A. Như vậy nó sẽ trả về bài j, k, l, m, n ,… Mà trong khi đó bài j đã được trình bày ra rồi, bây giờ thế này nó lại có thêm một cái y hệt! Tuy nhiên với cách 1 thì tình huống trớ trêu này không xảy ra được


Như vậy, em nên chỉnh sửa cách làm của mình như thế nào để không bị như vậy ạ?
Và nếu có ai có thể cho em thấy cách scroll load của diễn đàn như thế nào thì tuyệt quá

Hung viết 12:59 ngày 01/10/2018

em sử dụng phương thức append() của jQuery.

Vấn đề ở chỗ này nè bạn.
Có 2 cách cập nhật list:

  • Cập nhật toàn bộ list, là cách 1 của bạn.
  • Chỉ cập nhật các post cần thiết.

Cách thứ 2 thì yêu cầu mỗi post phải có thuộc tính id để phân biệt mỗi post trong danh sách. Khi bạn nhận được danh sách post từ AJAX trả về, thì danh sách đó phải loại bỏ các id trùng trước khi append().

Sau này, khi bạn làm về danh sách thì nên có thêm thuộc tính id cho mỗi record trong danh sách.

Ngô Quang Dương viết 13:06 ngày 01/10/2018

Hôm nay mình đã nghĩ ra cách khác
Mình không sử dụng truy vấn LIMIT nữa mà thay vào đó là lấy toàn bộ ID và sắp xếp theo thứ tự từ mới đến cũ

SELECT postId FROM posts ORDER BY lastActivity DESC

Tuy lấy ra toàn bộ nhưng chỉ lấy có ID :3 - truy vấn này chỉ sử dụng khi trang được load lại. Khi trang load xong, lưu lại kết quả truy vấn vào một mảng để sử dụng.
Cuối cùng, để phân trang thì PHP có hàm array_slice() để lấy ra 10 giá trị liên tiếp của mảng, bắt đầu từ một số thứ tự cụ thể và chỉ cần trả về những bài có id thuộc 10 giá trị đó. Rồi từ 10 id đó mới lấy ra dữ liệu của 10 bài tương ứng.
Cách này đảm bảo việc mỗi lần load sẽ load được đúng 10 bài(trừ khi đã đến cuối) nhưng việc lấy ra chừng đó ID đến một lúc nào đó sẽ tràn bộ nhớ.

Bài liên quan
0