Accupass 瀏覽擴充功能,使用 Javascript + TamperMonkey 自製自動加載頁面
Accupass 活動通,是一個集結各式活動並販售活動票券網站(亦可參考另一個同類型的網站 KKTIX),自己蠻常在活動通上瀏覽活動,讓自己的下班生活能夠多點變化,接觸職場沒機會接觸的議題與活動。
但該網站的瀏覽模式用久會很惱人,那活動分頁模式非常難用,必須得用滑鼠去找下一頁的按鈕,重新捲動頁面到網頁頂端,才能看完新一頁的活動資訊,這是多麼消耗注意力啊。難道沒有辦法向 Pinterest 那樣的瀑布式瀏覽嗎?自動加載下一頁的活動資訊,只要一直按下 Space 鍵捲動頁面到頁面底部,持續加載活動資訊,就可以看完指定搜尋條件的活動列表。
因此寫了封信詢問 Accupass 團隊,有沒有機會可以改成如此的瀏覽模式,但獲得是如同罐頭訊息的回覆……。身為一個軟體工程師,既然沒人可以幫忙解決自己的痛點,那麼就自己嘗試做一個吧。從 Chrome developer console 中分析,活動通採用 Angular 前端框架,自己完全陌生的框架,真的不知道如何起手,不知道如何找到 binding 函數,如何將資料與 view binding 在一起呢?
想想先從自己熟悉 jQuery 開始吧,於是弄出以下的簡易 Script,能安裝在 TamperMonkey。TamperMonkey 是一個 Chrome 擴充套件,能管理瀏覽特定網頁時,加載執行已設定的用戶端腳本,腳本用來調整網頁內容,讓用戶瀏覽體驗更好甚至更方便。雖然做法上有點暴力,但至少有達到預先設想的目地。
// ==UserScript==
// @name Accupass Infinite Scrolling
// @name:zh-TW 活動通 (Accupass) 無限捲頁 (自動加載下一頁)
// @namespace http://tampermonkey.net/
// @version 0.4
// @description Load next event page automatically when scroll to bottom of the page on [Accupass](https://www.accupass.com/).
// @description:zh-TW 在[活動通]((https://www.accupass.com/))瀏覽活動時,當頁面捲到頁面底部,將會自動加載下一頁活動內容,而不用手動點擊分頁按鈕
// @author Siyuan
// @match https://old.accupass.com/search/*
// @grant none
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/URI.js/1.18.10/URI.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js
// ==/UserScript==
(function($, undefined) {
$(function() {
var loading = false;
var initNexturl = false;
var nexturl = null;
$(document).ready(function() {
// repack pushState for clearing loading page when changed search arguments
(function(history) {
var pushState = history.pushState;
history.pushState = function(state) {
initNexturl = false;
nexturl = null;
return pushState.apply(history, arguments);
};
})(window.history);
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).height() > $(document).height() - 600) {
doLoadNext();
}
});
});
function fixAuccpassStupidErr(uri) {
// Fixed date error cause by Accupass .../search/changeconditions/r/0/0/6/0/4/1/20170727/20170713?q= to /search/changeconditions/r/0/0/6/0/4/1/20170713/20170727?q=
var u = new URI(uri);
var d = u.pathname().split('/');
if (d.length >= 2) {
var d1 = moment(d[d.length - 2], 'YYYYMMDD');
var d2 = moment(d[d.length - 1], 'YYYYMMDD');
if (d1.isValid() && d2.isValid()) {
if (!d1.isBefore(d2)) {
d[d.length - 2] = d2.format('YYYYMMDD');
d[d.length - 1] = d1.format('YYYYMMDD');
u.pathname(d.join('/'));
}
}
}
return u.toString();
}
function getNextPageUrl(dom) {
if (!dom) {
dom = $(document);
}
var a = dom.find("ul.pagination li.active");
if (a.length) {
a = a.next();
if (a.length) {
var h = a.find('a').attr('href');
if (h.length) {
if (h.length >= 7) {
if (h.indexOf('/search/') === 0) {
h = h.substring(0, 7) + '/changeconditions' + h.substring(7);
return fixAuccpassStupidErr(h);
}
}
}
}
return '';
}
return null;
}
function doLoadNext() {
if (loading)
return;
if (!initNexturl) {
nexturl = getNextPageUrl();
initNexturl = nexturl !== null;
if (initNexturl) {
console.log("Next: " + nexturl);
}
}
if (!nexturl)
return;
loading = true;
// Not sure how to bind data within angular view, so use string replace to build final HTML here
var previousSibling = $('section div[ga-hover="Page"]');
$loader = $('<div class="clearfix"/><div class="row" style="text-align: center;">Loading...<img src="https://i0.wp.com/cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif?resize=24%2C24"></div>');
$loader.insertBefore(previousSibling);
$.ajax({
url: nexturl,
}).always(function() {
loading = false;
$loader.remove();
}).done(function(html) {
var dom = $($.parseHTML(html));
var template = `
<div class="apcss-activity-card-header">
<a class="apcss-activity-card-image" href="/event/register/JSON:eventIdNumber" target="_self">
<img alt="JSON:name" src="JSON:photoUrl">
</a>
<span class="apcss-activity-pageview">
<i class="icon-eye-open"></i>JSON:pageview
</span>
</div>
<div class="apcss-activity-card-body">
<a href="/event/register/JSON:eventIdNumber" target="_self">
<h3 class="apcss-activity-card-title">JSON:name</h3>
</a>
<p class="apcss-activity-card-date">JSON:fullDateTimeStr</p>
JSON:summary
</div>
<div class="apcss-activity-card-footer">
<div class="row">
<div class="col-xs-6">
<i class="icon-heart"></i>
<span class="apcss-activity-card-like"> JSON:likeCount Likes</span>
</div>
<div class="col-xs-6">JSON:StateButton
</div>
</div>
</div>
`;
dom.find('div[event-card]').each(function() {
var raw = $(this).attr('event-row');
var e = JSON.parse(raw);
var bh = template;
var buttonTemplate = '';
if (e.remainingTicket > 0) {
if (e.eventCardStatus == 2) {
buttonTemplate = '<a class="apcss-btn apcss-btn-block activity-card-status-ready" target="_self" href="/event/register/JSON:eventIdNumber">On Sale (JSON:remainingTicket)</a>';
} else {
buttonTemplate = '<a class="apcss-btn apcss-btn-block activity-card-status-hot" target="_self" href="/event/register/JSON:eventIdNumber">On Sale (JSON:remainingTicket)</a>';
}
} else {
buttonTemplate = '<a class="apcss-btn apcss-btn-block activity-card-status-end" target="_self" href="/event/register/JSON:eventIdNumber">Sold Out</a>';
}
bh = bh.replace(/JSON:StateButton/g, buttonTemplate);
bh = bh.replace(/JSON:remainingTicket/g, e.remainingTicket);
bh = bh.replace(/JSON:eventIdNumber/g, e.eventIdNumber);
bh = bh.replace(/JSON:name/g, e.name);
bh = bh.replace(/JSON:photoUrl/g, e.photoUrl);
bh = bh.replace(/JSON:pageview/g, e.pageview);
bh = bh.replace(/JSON:fullDateTimeStr/g, e.fullDateTimeStr);
bh = bh.replace(/JSON:summary/g, e.summary);
bh = bh.replace(/JSON:likeCount/g, e.likeCount);
$(this).addClass('apcss-activity-card');
$(this).append($(bh));
$(this).parent().insertBefore(previousSibling);
});
nexturl = getNextPageUrl(dom);
if (nexturl !== null && nexturl.length <= 0) {
console.log("End");
$end = $('<div class="clearfix"/><div class="row" style="text-align: center;">--End--</div>');
$end.insertBefore(previousSibling);
} else {
console.log("Next: " + nexturl);
}
});
}
});
})(window.jQuery.noConflict(true));
該程式碼腳本也上傳到 Github,並將其加入到 Greasyfork,該網站有許多 TamperMonkey 全球開發者貢獻的腳本。可點選以下連結前往安裝或是查看最新的程式碼:
沒有留言: