Làm thế nào để xây dựng một trải nghiệm Infinite Scroll với History Web API
() translation by (you can also view the original English article)
Trong hướng dẫn này, chúng tôi sẽ củng cố các kỹ năng API Web History của chúng ta. Chúng ta sẽ xây dựng một UX pattern trên Web, kiểu được ưa chuộng và không được ưa chuộng với cùng một thước đo: infinite scrolling.
Infinite scrolling là một pattern giao diện để tải nội dung mới khi chúng ta tiến đến phần cuối trang web. Infinite scrolling được cho là có thể giữ lại người dùng khi được triển khai tỉ mĩ; một số hình mẫu điển hình là các nền tảng xã hội như Facebook, Twitter và Pinterest.
Tuy nhiên, điều đáng chú ý là chúng tôi đang nỗ lực cải thiện từ những gì chúng tôi đã xây dựng trong một bài hướng dẫn trước đó, Lovely, Smooth Page Transitions With the History Web API. Trong hướng dẫn này, chúng tôi sẽ giải quyết tương tác xử lý cuộn trang của người dùng, điều này có thể xảy ra với tần suất rất thường xuyên. Nếu chúng tôi không cẩn thận với code của chúng tôi, điều này sẽ gây tác động bất lợi cho hiệu suất của website. Hãy chắc rằng bạn đã đọc các hướng dẫn trước đây trước thử thực hiện triển khai này, chỉ để cho bạn hiểu rõ về những điều chúng tôi sắp thực hiện.
- WordPressCách tích hợp SmoothState.js vào WordPress theme.Thoriq Firdaus
- JavaScriptLovely, Smooth Page Transitions With the History Web APIThoriq Firdaus
Tuy nhiên, nếu bạn hào hứng với ý tưởng thử thách này, hãy thắt dây an toàn, chuẩn bị sẵn sàng và bắt đầu!
Xây dựng website demo
Website của chúng tôi là một blog tĩnh. Bạn có thể xây dựng bằng HTML thuần túy hoặc tận dụng trình tạo trang website tĩnh như Jekyll, Middleman hoặc Hexo. Demo của chúng tôi cho hướng dẫn này trông như sau:



Có một vài điều liên quan đến cấu trúc HTML cần sự chú ý của bạn.
1 |
<!-- site header -->
|
2 |
<div class="content" id="main"> |
3 |
<article class="article" id="article-5" data-article-id="5"> |
4 |
<!-- content -->
|
5 |
</article>
|
6 |
<!-- site footer -->
|
- Như bạn có thể thấy từ đoạn code bên trên, phần bài viết cần được nằm trong trong một phần tử HTML có ID duy nhất. Bạn có thể sử dụng phần tử
div
hoặcsection
, không có hạn chế khi đặt tênid
cho phần tử đó. - Ngoài ra, trên chính phần nội dung, bạn sẽ cần thêm thuộc tính
data-article-id
có chứaid
tương ứng của bài viết.
Hãy thoải mái kết hợp theo style của website; khiến nó đầy màu sắc, hấp dẫn hoặc bổ sung thêm nội dung.
Tải JavaScript
Để bắt đầu, hãy tải các thư viện JavaScript sau theo thứ tự sau vào mỗi trang trong blog của bạn.
- jquery.js: thư viện mà chúng ta sẽ sử dụng để chọn các phần tử, thêm nội dung mới, thêm class mới và thực hiện các yêu cầu AJAX.
- history.js: một polyfill xử lý history API nguyên thuỷ của các trình duyệt.
Plugin jQuery tự xây dựng của chúng tôi
Ngoài hai điều này, chúng tôi sẽ cần nạp file JavaScript của riêng chúng ta, ở đó có viết các tập lệnh để thực hiện infinite scrolling. Cách tiếp cận chúng tôi sẽ thực hiện là đưa JavaScript của chúng tôi vào một plugin jQuery, thay vì viết nó trực tiếp như chúng tôi đã làm trong các hướng dẫn trước.
Chúng ta sẽ bắt đầu plugin với jQuery Plugin Boilerplate. Điều này giống với HTML5 Boilerplate ở chỗ cung cấp một bộ sưu tập các template, các pattern và các thực tiễn hay nhất để xây dựng một plugin jQuery.
Tải xuống Boilerplate, đặt vào thư mục của website của bạn,nơi chứa tất cả các file JavaScript (chẳng hạn như /assets/js/)
và đổi tên file thành “keepscrolling.jquery.js” (tên này lấy cảm hứng từ Dory của Finding Nemo và câu nói nổi tiếng của cô ấy “Keep Swimming”).
1 |
assets/js |
2 |
├── keepscrolling.jquery.js |
3 |
├── keepscrolling.jquery.js.map |
4 |
└── src |
5 |
└── keepscrolling.jquery.js |
Plugin giới thiệu sự linh hoạt với Options hoặc Settings.
Quan sát cấu trúc Plugin jQuery
Việc viết một plugin jQuery yêu cầu cách suy nghĩ hơi khác một chút, vì vậy trước tiên chúng ta sẽ xem xét cách jQuery plugin của chúng ta được cấu trúc ra sao trước khi chúng ta bổ sung vào bất kỳ đoạn code nào. Bạn có thể thấy ở dưới, tôi chia code thành bốn phần:
1 |
;( function( $, window, document, undefined ) { |
2 |
|
3 |
"use strict"; |
4 |
|
5 |
// 1.
|
6 |
var pluginName = "keepScrolling", |
7 |
defaults = {}; |
8 |
|
9 |
// 2.
|
10 |
function Plugin ( element, options ) { |
11 |
|
12 |
this.element = element; |
13 |
this.settings = $.extend( {}, defaults, options ); |
14 |
|
15 |
this._defaults = defaults; |
16 |
this._name = pluginName; |
17 |
|
18 |
this.init(); |
19 |
}
|
20 |
|
21 |
// 3.
|
22 |
$.extend( Plugin.prototype, { |
23 |
init: function() { |
24 |
console.log( "Plugin initialized" ); |
25 |
},
|
26 |
} ); |
27 |
|
28 |
// 4.
|
29 |
$.fn[ pluginName ] = function( options ) { |
30 |
return this.each( function() { |
31 |
if ( !$.data( this, "plugin_" + pluginName ) ) { |
32 |
$.data( this, "plugin_" + |
33 |
pluginName, new Plugin( this, options ) ); |
34 |
}
|
35 |
} ); |
36 |
};
|
37 |
|
38 |
} )( jQuery, window, document ); |
- Trong phần đầu tiên của code, chúng tôi chỉ định tên plugin của chúng tôi,
keepScrolling
, với "camel case" (viết hoa các chữ cái đầu tiên) theo quy ước đặt tên phổ biến của JavaScript. Chúng tôi cũng có một biến,defaults
, sẽ chứa các thiết lập mặc định của plugin. - Tiếp theo, chúng tôi có chức năng chính của plugin,
Plugin()
. Hàm này có thể so sánh với một “constructor”, trong trường hợp này, khởi tạo plugin và đồng nhất thiết lập defaults với bất kỳ thiết lập được truyền vào khi khởi tạo. - Phần thứ ba là nơi chúng ta sẽ tạo các hàm riêng của chúng ta để phục vụ chức năng infinite scrolling.
- Cuối cùng, phần thứ tư là phần kết thúc toàn bộ nội dung của một plugin jQuery.
Với tất cả, giờ chúng ta có thể viết JavaScript của mình. Và chúng tôi bắt đầu bằng cách xác định các tùy chọn default của plugin.
Các tùy chọn
1 |
;( function( $, window, document, undefined ) { |
2 |
|
3 |
"use strict"; |
4 |
|
5 |
var pluginName = "keepScrolling", |
6 |
defaults = { |
7 |
floor: null, |
8 |
article: null, |
9 |
data: {} |
10 |
};
|
11 |
...
|
12 |
|
13 |
} )( jQuery, window, document ); |
Như bạn có thể thấy ở trên, chúng tôi đã đặt ba tùy chọn được trình bày:
-
floor
: một id selector - chẳng hạn như#floor
hoặc#footer
- chúng được xem như là phần kết thúc của nội dung. Thông thường, nó sẽ là phần footer của website. -
article
: một class selector bao phủ bài viết. -
data
: vì chúng ta không truy cập bất kỳ API ngoại tuyến (đây là website tĩnh), chúng ta cần chuyển tập hợp dữ liệu bài viết gồm article URL, ID, và title ở dạng JSON làm đối số tùy chọn này.
Các hàm
Ở đây chúng ta có init()
. Trong hàm này, chúng ta sẽ thêm vào một số hàm cần chạy ngay lập tức trong quá trình khởi tạo trang. Ví dụ: chúng tôi chọn site floor.
1 |
$.extend( Plugin.prototype, { |
2 |
|
3 |
// The `init()` function.
|
4 |
init: function() { |
5 |
this.siteFloor = $( this.settings.floor ); // select the element set as the site floor. |
6 |
},
|
7 |
} ); |
Ngoài ra còn có một vài hàm mà chúng tôi sẽ chạy sau khi khởi tạo. Chúng tôi thêm các hàm để tạo ra và bổ sung sau hàm init
.
Tập hợp các hàm đầu tiên mà chúng ta sẽ viết là các hàm chúng ta sử dụng để lấy ra hoặc trả về một "giá trị"; bất cứ gì từ một String, một Object, hoặc một Number sẽ được sử dụng nhiều lần xuyên suốt các hàm khác trong plugin. Chúng được dùng để:
Lấy tất cả các bài viết trên trang:
1 |
/**
|
2 |
* Find and returns list of articles on the page.
|
3 |
* @return {jQuery Object} List of selected articles.
|
4 |
*/
|
5 |
getArticles: function() { |
6 |
return $( this.element ).find( this.settings.article ); |
7 |
},
|
Lấy địa chỉ bài viết. Trong WordPress, điều này thường được gọi là “post slug”.
1 |
/**
|
2 |
* Returns the article Address.
|
3 |
* @param {Integer} i The article index.
|
4 |
* @return {String} The article address, e.g. `post-two.html`
|
5 |
*/
|
6 |
getArticleAddr: function( i ) { |
7 |
|
8 |
var href = window.location.href; |
9 |
var root = href.substr( 0, href.lastIndexOf( "/" ) ); |
10 |
|
11 |
return root + "/" + this.settings.data[ i ].address + ".html"; |
12 |
},
|
Lấy id bài viết tiếp theo và địa chỉ để truy xuất.
1 |
/**
|
2 |
* Return the "next" article.
|
3 |
* @return {Object} The `id` and `url` of the next article.
|
4 |
*/
|
5 |
getNextArticle: function() { |
6 |
|
7 |
// Select the last article.
|
8 |
var $last = this.getArticles().last(); |
9 |
|
10 |
var articlePrevURL; |
11 |
|
12 |
/**
|
13 |
* This is a simplified way to determine the content ID.
|
14 |
*
|
15 |
* Herein, we substract the last post ID by `1`.
|
16 |
* Ideally, we should be calling call an API endpoint, for example:
|
17 |
* https://www.techinasia.com/wp-json/techinasia/2.0/posts/329951/previous/
|
18 |
*/
|
19 |
var articleID = $last.data( "article-id" ); |
20 |
var articlePrevID = parseInt( articleID, 10 ) - 1; // Previous ID |
21 |
|
22 |
// Loop into the Option `data`, and get the correspending Address.
|
23 |
for ( var i = this.settings.data.length - 1; i >= 0; i-- ) { |
24 |
if ( this.settings.data[ i ].id === articlePrevID ) { |
25 |
articlePrevURL = this.getArticleAddr( i ) ; |
26 |
}
|
27 |
}
|
28 |
|
29 |
return { |
30 |
id: articlePrevID, |
31 |
url: articlePrevURL |
32 |
};
|
33 |
},
|
Sau đây là các hàm tiện ích của plugin; mỗi hàm chịu trách nhiệm thực hiện một "nhiệm vụ" cụ thể. Bao gồm:
Một hàm cho biết liệu một phần tử có đang tiến vào viewport. Chúng ta chủ yếu sử dụng nó để cho biết nếu liệu floor của trang có được nhìn thấy trong viewport không.
1 |
/**
|
2 |
* Detect whether the target element is visible.
|
3 |
* https://stackoverflow.com/q/123999/
|
4 |
*
|
5 |
* @return {Boolean} `true` if the element in viewport, and `false` if not.
|
6 |
*/
|
7 |
isVisible: function() { |
8 |
if ( target instanceof jQuery ) { |
9 |
target = target[ 0 ]; |
10 |
}
|
11 |
|
12 |
var rect = target.getBoundingClientRect(); |
13 |
|
14 |
return rect.bottom > 0 && |
15 |
rect.right > 0 && |
16 |
rect.left < ( window.innerWidth || document.documentElement.clientWidth ) && |
17 |
rect.top < ( window.innerHeight || document.documentElement.clientHeight ); |
18 |
}, |
Một hàm tạm dừng việc thực thi hàm; được gọi là debounce. Như đã đề cập trước đó, chúng tôi sẽ đối mặt với hành động scrolling của người dùng sẽ diễn ra rất thường xuyên. Do đó, một hàm trong sự kiện scroll
sẽ chạy thường xuyên, sau khi người dùng scroll, điều này sẽ làm cho trải nghiệm scroll trên trang web không được mượt mà.
Hàm debounce ở trên sẽ giảm tần suất xử lý xuống. Nó sẽ đợi thời gian được chỉ định, thông qua tham số wait
, sau khi người dùng thao các cuộn trước khi chạy hàm.
1 |
/**
|
2 |
* Returns a function, that, as long as it continues to be invoked, will not b
|
3 |
* triggered.
|
4 |
* The function will be called after it stops being called for N milliseconds.
|
5 |
* If immediate is passed, trigger the function on the leading edge, instead of
|
6 |
* the trailing.
|
7 |
*
|
8 |
* @link https://davidwalsh.name/function-debounce
|
9 |
* @link http://underscorejs.org/docs/underscore.html#section-83
|
10 |
*
|
11 |
* @param {Function} func Function to debounce
|
12 |
* @param {Integer} wait The time in ms before the Function run
|
13 |
* @param {Boolean} immediate
|
14 |
* @return {Void}
|
15 |
*/
|
16 |
isDebounced: function( func, wait, immediate ) { |
17 |
var timeout; |
18 |
|
19 |
return function() { |
20 |
|
21 |
var context = this, |
22 |
args = arguments; |
23 |
|
24 |
var later = function() { |
25 |
timeout = null; |
26 |
if ( !immediate ) { |
27 |
func.apply( context, args ); |
28 |
}
|
29 |
};
|
30 |
|
31 |
var callNow = immediate && !timeout; |
32 |
|
33 |
clearTimeout( timeout ); |
34 |
timeout = setTimeout( later, wait ); |
35 |
|
36 |
if ( callNow ) { |
37 |
func.apply( context, args ); |
38 |
}
|
39 |
};
|
40 |
}, |
Một hàm xác định có tiến hành hoặc hủy bỏ một xử lý hay không.
1 |
/**
|
2 |
* Whether to proceed ( or not to ) fetching a new article.
|
3 |
* @return {Boolean} [description]
|
4 |
*/
|
5 |
isProceed: function() { |
6 |
|
7 |
if ( articleFetching // check if we are currently fetching a new content. |
8 |
|| articleEnding // check if no more article to load. |
9 |
|| !this.isVisible( this.siteFloor ) // check if the defined "floor" is visible. |
10 |
) { |
11 |
return; |
12 |
}
|
13 |
|
14 |
if ( this.getNextArticle().id <= 0 ) { |
15 |
articleEnding = true; |
16 |
return; |
17 |
}
|
18 |
|
19 |
return true; |
20 |
},
|
Chúng tôi sẽ sử dụng hàm tiện ích trước, isProceed()
, để kiểm tra xem liệu tất cả các điều kiện có được đáp ứng để tiếp tục lấy thêm ra nội dung mới hay không. Nếu có, hàm theo sau đó sẽ chạy, tìm nạp nội dung mới và thêm nó sau bài viết cuối cùng.
1 |
/**
|
2 |
* Function to fetch and append a new article.
|
3 |
* @return {Void}
|
4 |
*/
|
5 |
fetch: function() { |
6 |
|
7 |
// Shall proceed or not?
|
8 |
if ( !this.isProceed() ) { |
9 |
return; |
10 |
}
|
11 |
|
12 |
var main = this.element; |
13 |
var $articleLast = this.getArticles().last(); |
14 |
|
15 |
$.ajax( { |
16 |
url: this.getNextArticle().url, |
17 |
type: "GET", |
18 |
dataType: "html", |
19 |
beforeSend: function() { |
20 |
articleFetching = true; |
21 |
}
|
22 |
} ) |
23 |
|
24 |
/**
|
25 |
* When the request is complete and it successly
|
26 |
* retrieves the content, we append the content.
|
27 |
*/
|
28 |
.done( function( res ) { |
29 |
$articleLast
|
30 |
.after( function() { |
31 |
if ( !res ) { |
32 |
return; |
33 |
}
|
34 |
return $( res ).find( "#" + main.id ).html(); |
35 |
} ); |
36 |
} ) |
37 |
|
38 |
/**
|
39 |
* When the function is complete, whether it `fail` or `done`,
|
40 |
* always set the `articleFetching` to false.
|
41 |
* It specifies that we are done fetching the new content.
|
42 |
*/
|
43 |
.always( function() { |
44 |
articleFetching = false; |
45 |
} ); |
46 |
},
|
Thêm hàm này trong init
. Vì vậy, hàm này sẽ chạy ngay sau khi các plugin được khởi tạo, và sau đó lấy nội dung mới khi các điều kiện được đáp ứng.
1 |
init: function() { |
2 |
this.siteFloor = $( this.settings.floor ); // select the element set as the site floor. |
3 |
this.fetch(); |
4 |
},
|
Tiếp theo, chúng tôi sẽ thêm một hàm để thay đổi lịch sử trình duyệt bằng History Web API. Hàm đặc biệt này phức tạp hơn các hàm trước của chúng ta. Phần khó khăn ở đây là chính xác lúc nào chúng ta nên thay đổi lịch sử trong thao tác cuộn của người dùng, tiêu đề tài liệu, cũng như URL. Sau đây là minh họa để giúp đơn giản hóa ý tưởng đằng sau hàm:



Như bạn có thể thấy từ hình vẽ, chúng tôi có ba dòng: “roof-line”, “mid-line” và “floor-line” để minh họa vị trí bài viết trong khung nhìn. Hình ảnh cho thấy phần cuối cùng của bài viết đầu tiên, cũng như phần đầu của bài viết thứ hai, hiện giờ nằm ở mid-line. Nó không cụ thể ý định của người dùng về bài viết họ đang xem; nó là bài viết đầu tiên hay là bài đăng thứ hai? Do đó, chúng tôi sẽ không thay đổi lịch sử trình duyệt khi hai bài viết ở vị trí này.
Chúng tôi sẽ ghi lại lịch sử của bài viết tiếp theo khi bài viết đạt đến “roof-line”, vì nó chiếm phần lớn phần hiển thị của viewport.



Chúng tôi ghi lại lịch sử của bài đăng trước đó khi phần dưới của nó chạm vào “floor-line”, tương tự, vì giờ đây phần lớn phần hiển thị của viewport.



Đây là code bạn sẽ cần bổ sung vào:
1 |
init: function() { |
2 |
this.roofLine = Math.ceil( window.innerHeight * 0.4 ); // set the roofLine; |
3 |
this.siteFloor = $( this.settings.floor ); |
4 |
this.fetch(); |
5 |
},
|
6 |
/**
|
7 |
* Change the browser history.
|
8 |
* @return {Void}
|
9 |
*/
|
10 |
history: function() { |
11 |
|
12 |
if ( !window.History.enabled ) { |
13 |
return; |
14 |
}
|
15 |
|
16 |
this.getArticles() |
17 |
.each( function( index, article ) { |
18 |
|
19 |
var scrollTop = $( window ).scrollTop(); |
20 |
var articleOffset = Math.floor( article.offsetTop - scrollTop ); |
21 |
|
22 |
if ( articleOffset > this.threshold ) { |
23 |
return; |
24 |
}
|
25 |
|
26 |
var articleFloor = ( article.clientHeight - ( this.threshold * 1.4 ) ); |
27 |
articleFloor = Math.floor( articleFloor * -1 ); |
28 |
|
29 |
if ( articleOffset < articleFloor ) { |
30 |
return; |
31 |
}
|
32 |
|
33 |
var articleID = $( article ).data( "article-id" ); |
34 |
articleID = parseInt( articleID, 10 ); |
35 |
|
36 |
var articleIndex; |
37 |
for ( var i = this.settings.data.length - 1; i >= 0; i-- ) { |
38 |
if ( this.settings.data[ i ].id === articleID ) { |
39 |
articleIndex = i; |
40 |
}
|
41 |
}
|
42 |
|
43 |
var articleURL = this.getArticleAddr( articleIndex ); |
44 |
|
45 |
if ( window.location.href !== articleURL ) { |
46 |
var articleTitle = this.settings.data[ articleIndex ].title; |
47 |
window.History.pushState( null, articleTitle, articleURL ); |
48 |
}
|
49 |
}.bind( this ) ); |
50 |
},
|
Cuối cùng, chúng ta tạo một hàm sẽ chạy hàm fetch()
và history()
khi người dùng đang cuộn trang. Để làm như vậy, chúng ta tạo ra một hàm mới gọi là scroller()
và chạy nó lúc khởi tạo plugin.
1 |
/**
|
2 |
* Functions to run during the scroll.
|
3 |
* @return {Void}
|
4 |
*/
|
5 |
scroller: function() { |
6 |
window.addEventListener( "scroll", this.isDebounced( function() { |
7 |
this.fetch(); |
8 |
this.history(); |
9 |
}, 300 ).bind( this ), false ); |
10 |
}
|
Và như bạn có thể thấy ở trên, chúng tôi debounce những điều này khi việc thực thi AJAX và thay đổi lịch sử trình duyệt là một hoạt động tốn công.
Thêm một Content Placeholder
Đây là phần tùy chọn, nhưng được khuyến khích cho trải nghiệm người dùng. Placeholder cung cấp phản hồi cho người dùng, báo hiệu rằng một bài viết mới sắp ra đời.
Đầu tiên, chúng tôi tạo template cho placeholder. Thông thường, loại template này được đặt sau footer của site.
1 |
<script type="text/template" id="tmpl-placeholder"> |
2 |
<div class="placeholder placeholder--article" id="placeholder-article"> |
3 |
<div class="container"> |
4 |
<div class="placeholder__header animated"> |
5 |
<h1></h1> |
6 |
</div> |
7 |
<div> |
8 |
<p class="placeholder__p-1 animated"></p> |
9 |
<p class="placeholder__p-2 animated"></p> |
10 |
</div> |
11 |
</div> |
12 |
</div> |
13 |
</script>
|
Hãy nhớ rằng cấu trúc placeholder cho bài viết phải giống với nội dung thực của blog của bạn. Điều chỉnh cấu trúc HTML cho phù hợp.
Style của placeholder đơn giản hơn. Nó bao gồm tất cả style cơ bản để bố trí nó giống như bài viết thực tế, hoạt cảnh @keyframe
mô phỏng cảm giác đang tải (loading) và style đổi chế độ hiển thị (placeholder ban đầu bị ẩn; chỉ được hiển thị khi phần tử cha có có class fetching
).
1 |
.placeholder { |
2 |
color: @gray-light; |
3 |
padding-top: 60px; |
4 |
padding-bottom: 60px; |
5 |
border-top: 6px solid @gray-lighter; |
6 |
display: none; |
7 |
.fetching & { |
8 |
display: block; |
9 |
}
|
10 |
p { |
11 |
display: block; |
12 |
height: 20px; |
13 |
background: @gray-light; |
14 |
}
|
15 |
&__header { |
16 |
animation-delay:.1s; |
17 |
h1 { |
18 |
height: 30px; |
19 |
background-color: @gray-light; |
20 |
}
|
21 |
}
|
22 |
&__p-1 { |
23 |
animation-delay:.2s; |
24 |
width: 80%; |
25 |
}
|
26 |
&__p-2 { |
27 |
animation-delay:.3s; |
28 |
width: 70%; |
29 |
}
|
30 |
}
|
Sau đó, chúng tôi cập nhật một vài dòng để hiển thị placeholder xuyên suốt AJAX request, như sau.
1 |
/**
|
2 |
* Initialize.
|
3 |
* @return {Void}
|
4 |
*/
|
5 |
init: function() { |
6 |
|
7 |
this.roofLine = Math.ceil( window.innerHeight * 0.4 ); |
8 |
this.siteFloor = $( this.settings.floor ); |
9 |
|
10 |
this.addPlaceholder(); |
11 |
|
12 |
this.fetch(); |
13 |
this.scroller(); |
14 |
},
|
15 |
|
16 |
/**
|
17 |
* Append the addPlaceholder.
|
18 |
* Placeholder is used to indicate a new post is being loaded.
|
19 |
* @return {Void}
|
20 |
*/
|
21 |
addPlaceholder: function() { |
22 |
|
23 |
var tmplPlaceholder = document.getElementById( "tmpl-placeholder" ); |
24 |
tmplPlaceholder = tmplPlaceholder.innerHTML; |
25 |
|
26 |
$( this.element ).append( tmplPlaceholder ); |
27 |
},
|
28 |
|
29 |
/**
|
30 |
* Function to fetch and append a new article.
|
31 |
* @return {Void}
|
32 |
*/
|
33 |
fetch: function() { |
34 |
...
|
35 |
|
36 |
// Select the element wrapping the article.
|
37 |
var main = this.element; |
38 |
|
39 |
$.ajax( { |
40 |
|
41 |
...
|
42 |
|
43 |
beforeSend: function() { |
44 |
|
45 |
...
|
46 |
// Add the 'fetching' class.
|
47 |
$( main ).addClass( function() { |
48 |
return "fetching"; |
49 |
} ); |
50 |
}
|
51 |
} ) |
52 |
|
53 |
...
|
54 |
|
55 |
.always( function() { |
56 |
|
57 |
...
|
58 |
// Remove the 'fetching' class.
|
59 |
$( main ).removeClass( function() { |
60 |
return "fetching"; |
61 |
} ); |
62 |
} ); |
Đó là cách chúng tôi xử lý placeholder! Plugin của chúng tôi đã hoàn tất và đây là lúc triển khai plugin.
Triển khai
Việc triển khai plugin khá đơn giản. Chúng tôi chỉ định element (phần tử) kết thúc bài viết trên blog của chúng tôi và gọi plugin với các tùy chọn được đặt như sau.
1 |
$( document ).ready( function() { |
2 |
$( "#main" ).keepScrolling({ |
3 |
floor: "#footer", |
4 |
article: ".article", |
5 |
data : [{ |
6 |
"id": 1, |
7 |
"address": "post-one", |
8 |
"title": "Post One" |
9 |
}, { |
10 |
"id": 2, |
11 |
"address": "post-two", |
12 |
"title": "Post Two" |
13 |
}, { |
14 |
"id": 3, |
15 |
"address": "post-three", |
16 |
"title": "Post Three" |
17 |
}, { |
18 |
"id": 4, |
19 |
"address": "post-four", |
20 |
"title": "Post Four" |
21 |
}, { |
22 |
"id": 5, |
23 |
"address": "post-five", |
24 |
"title": "Post Five" |
25 |
}]
|
26 |
});
|
27 |
} ); |
Infinite scroll bây giờ sẽ hoạt động.



Lưu ý: nút Back (quay lai)
Trong hướng dẫn này, chúng tôi đã xây dựng trải nghiệm infinite scroll; hiệu ứng mà chúng ta thường thấy trên các trang tin tức như Quartz, TechInAsia và trong nhiều ứng dụng di động.
Mặc dù nó đã được chứng minh là một cách hiệu quả để níu giữ tương tác của người dùng, nó cũng có nhược điểm: phá vỡ nút "back" trong trình duyệt. Khi bạn nhấp vào nút đó, nó sẽ không hoàn toàn cuộn chính xác đến nội dung đã xem hoặc trang đã truy cập trước đó.
Trang web giải quyết vấn đề này theo nhiều cách khác nhau; Quartz, sẽ chuyển hướng bạn đến referred URL; URL mà bạn đã truy cập trước đó, nhưng không phải là URL được ghi lại thông qua Web History API. TechInAsia đơn giản chỉ đưa bạn trở lại trang chủ.
Tổng kết
Hướng dẫn này khá dài, đề cập đến nhiều thứ! Một số người trong số đó dễ hiểu, khi một số phần có thể không dễ dàng để hấp thu. Để giúp đỡ, tôi đã cóp nhặt một danh sách các tài liệu tham khảo để một bổ sung cho bài viết này.
- Manipulating the browser history
- Lovely, Smooth Page Transitions With the History Web API
- Can someone explain the debounce function in Javascript
- AJAX for Front-End Designers
- Phát triển plugin jQuery: Các phương pháp hay nhất
Cuối cùng, hãy xem mã nguồn đầy đủ và bản demo!