京東閱讀(web)體驗優化
- 2020 年 7 月 23 日
- 筆記
- javascript
京東有電子書可以購買,可以多端閱讀。比如PC客戶端,移動端,以及本文提到的PC網站端。
先換個鏡頭,讀書要記筆記(電子版本), 方便以後查閱。
鏡頭換回來,但是,我們為了方便肯定是想複製,下載啊,分享啊等,但是服務商一般是不允許你這麼做的。
我了,在京東買了幾本書,程序相關的,為了獲取好的體驗,在PC網站端閱讀, 發現精彩之處,想去複製到筆記裏面去。
結果,呵呵噠,結果連選中都不讓。
更關鍵的是,這代碼部分的顯示是這樣的。 辣眼睛啊。
所以,我打算hack一些,提升閱讀體驗。
- 允許選中
- 允許快捷複製, Control + C
- 允許右鍵複製
- 美化代碼
經過網頁的內容和節點分析,京東電子書PC網站端,是採用普通的div, p ,code等html標籤,而不是pdf的插件或者canvas等。
那麼我就有信心把你搞得面目全非,錯了,服服帖帖。
1. 允許選中
原理
是通過在div上的style user-select: none
來實現的
<div class="JD_page" style="width: 675px;overflow: hidden;height: 100%;float: left;background-color: rgb(240, 240, 240);margin-top: 5px;font-size: 16px;/* user-select: none; */z-index: 0;" ... >....</div>
方案
那麼就好辦了,音樂起。為了省去麻煩,來個暴力模式。
* {
user-select: auto !important;
}
之後,就是創建一個style的標籤,寫入樣式,掛載到head或者body裏面就ok拉。
2. 允許快捷複製
原理:
攔截keydown,讓你的鍵盤事件失靈。
方案:
- F12手動刪除註冊keydown的事件
- 代碼刪除註冊keydown的事件
這裡採用2方案,問題來了,如何找到某個元素註冊的事件。
chrome 控制台提供了一個 getEventListeners的方法,有那味了,戰歌起:
// 刪除監聽事件
function cRemoveListener(el, option) {
if (!el || !option) {
return;
}
el.removeEventListener(option.type, option.listener, option.useCapture || false);
}
// 刪除指定的監聽事件
function cRemoveListeners(el, eventName) {
var allListeners = getEventListeners(document);
var listeners = allListeners[eventName];
if (listeners && listeners.length > 0) {
for (let i = listeners.length - 1; i >= 0; i--) {
const lsOption = listeners[i];
cRemoveListener(el, lsOption);
}
}
}
// 允許 ctl + c 複製
// document.body keydown 事件
cRemoveListeners(document, "keydown");
3. 允許右鍵複製
原理
郵件菜單一般都是通過contextmenu事件,所以同上
方案
同允許快捷複製
// 允許右鍵
// document.body contextmenu 事件
cRemoveListeners(document, "contextmenu");
4. 美化代碼
原理
京東電子書,是對代碼部分使用code標籤來展示的。
方案
為了保持斷行,只需要使用pre標籤來包裹一下。
簡單的包裹會產生兩個問題
- 包裹一下後,代碼佔據的頁面內容會變成,而京東電子書這塊,限定了一個頁面的高度為900px,超過部分隱藏。
所以,我們在使用pre包裹code節點的同事,還需要調整頁面塊這裡的樣式。 - 電子是採取的分頁加載,在分頁加載之後,我們需要對新生成的code標籤進行包裹。
包裹code元素的思路
- 選擇出所有帶id的code節點(經過觀察,code節點分兩類,一類是有id標籤,一類是沒有,簡單說就是對應markdown裏面的 “` 和 `)
- 找到每個code節點的父節點
- 創建pre節點
- 插入pre節點到code節點之前
- code節點 掛載到 pre下
- code添加code-hacked class,標籤已經被hacked,避免重複被hacked
戰歌起,上代碼
// 創建節點
function createElement(tagName) {
const el = document.createElement(tagName);
return el;
}
// 包裹code節點
function adoptCodeNode(el) {
if (!el || el.tagName !== "CODE") return;
const parent = el.parentElement;
// 節點前插入
const preElement = createElement("pre");
preElement.classList.add("pre-hacked");
parent.insertBefore(preElement, el);
// 導入節點
preElement.appendChild(el);
el.classList.add("code-hacked");
};
function adoptAllCodes() {
const codesEls = Array.from(document.querySelectorAll("code[id]:not(.code-hacked)"));
for (let i = codesEls.length - 1; i >= 0; i--) {
adoptCodeNode(codesEls[i]);
}
}
adoptAllCodes();
分頁加載後的想到的方案
- 可以起個定時器,幾秒處理一下
- 監聽document.scrollingElement(document.body)的高度變化
- 監聽document.scrollingElement(document.body)的scroll事件
- 採用MutationObserver監聽子節點是否有變化
- 攔截分頁數據的HTTP請求
- 攔截執行滾動加載的事件
第一種方式簡單粗暴,其實我很喜歡。
第二種方式不太好實現,分頁加載後,window本身沒有觸發resize事件,window外的節點本身沒有監聽resize的能力(IE除外),當然可以通過 節點resize監聽, 但是高度的變化依舊沒法。
第三種方式,倒是可行,不過scroll事件觸發頻率很高,當然可以節流,也還不錯。
第四種 ,可行性高,PC兼容性行也不錯,性能也相對好一點。
第五種, 代碼複雜度會高一些。
戰歌起:
// 監聽高度變化
// //developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
function hackLoadmore() {
const targetNode = document.scrollingElement;
// 觀察器的配置(需要觀察什麼變動)
const config = { childList: true, subtree: true };
let preScrollHeight = targetNode.scrollHeight;
// 當觀察到變動時執行的回調函數
const callback = function (mutationsList, observer) {
// Use traditional 'for loops' for IE 11
console.log("MutationObserver");
for (let mutation of mutationsList) {
if (mutation.type !== 'childList') {
return;
}
const scollHeight = targetNode.scrollHeight
if (scollHeight == preScrollHeight) {
return;
}
preScrollHeight = scollHeight;
setTimeout(() => {
adoptAllCodes();
}, 2000)
}
};
// 創建一個觀察器實例並傳入回調函數
const observer = new MutationObserver(callback);
// 以上述配置開始觀察目標節點
observer.observe(targetNode, config);
}
基本的問題都解決了,上圖。
上圖可以看到
- 代碼已經格式化
- 可以右鍵選擇
當然ctrl + c這種效果用截圖是表達不出來的,得視頻,但是木有。
上圖,可以看到,因為代碼被格式化,頁面邊長,但是內容都已經能完整顯示。
最後,感謝大家的閱讀,也希望能幫助到大家。
哦,忘了,怎麼使用,還是截圖。