笔趣阁优化
// ==UserScript==
// @name 笔趣阁优化
// @namespace https://gitee.com/linhq1999/OhMyScript
// @version 5.2
// @description 专注阅读
// @author LinHQ
// @match http*://www.shuquge.com/*.html
// @exclude http*://www.shuquge.com/*index.html
// @match http*://www.sywx8.com/*.html
// @match http*://www.biqugetv.com/*.html
// @match http*://www.bqxs520.com/*.html
// @match https://www.dshfood.net/*.html
// @grant GM_addStyle
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @require https://greasyfork.org/scripts/427726-gbk-url-js/code/GBK_URLjs.js?version=953098
// @inject-into auto
// @license MIT
// ==/UserScript==
'use strict';
/** 配置示例
* 建议在定制 search 函数时, rq 函数始终把参数写全
* "sites": [
* {
* "desc": "shuquge", 网站链接关键字
* "url": "https://....", 网站首页链接
* "main": "div.reader", 主要部分选择器
* "title": ".reader h1", 标题选择器
* "txt": "#content", 文字部分选择器
* "toc": "dd a", 目录链接选择器
* "tocJump": 12, 跳过前面多少章
* "filter": ["div.header", "div.nav", "div.link"], 带有此选择器的元素将被删除
* "txtfilter": ["shuqu"] 带有此关键字的行将被删除
* "funcFilter"?: () => void, 自定义过滤器
* "nodash"?: boolean, 判断是否应该在书籍详情页链接后加额外的斜杠
* "search"?: (keywords: string, baseurl:string) => Promise<Link[]> 搜索行为
* }
* ]
*/
(() => {
// 缺省值,一般不用修改
const lineHeight = 1.3;
// const defaultFont = "楷体";
const defaultFont = "Source Han Sans SC VF";
let C = {
"sites": [
{
"desc": "shuquge",
"url": "https://www.shuquge.com/",
"main": "div.reader",
"title": ".reader h1",
"txt": "#content",
"toc": "dd a",
"tocJump": 12,
"filter": [
"div.header", "div.nav", "div.link", "img",
"#coupletleft", "#coupletright", "#HMRichBox"
],
"txtfilter": ["shuqu"],
"funcFilter": () => { var _a, _b; return (_b = (_a = fd(document, "#content")) === null || _a === void 0 ? void 0 : _a.previousSibling) === null || _b === void 0 ? void 0 : _b.remove(); }
},
{
"desc": "sywx",
"url": "https://www.sywx8.com/",
"main": "div#container",
"title": "div>h1",
"toc": "li a",
"tocJump": 0,
"txt": "div#BookText",
"filter": ["div.top", ".link.xb", "#footer"],
"txtfilter": ["最快更新", "松语", "本章完", "本章未完"],
// javascript 不支持 gbk 的 uri 编码,所以无法实现
// 但是用 gbk.js 就不一样了
"search": async (keywords, baseurl) => {
let links = [];
let doc = await rq({
"url": `https://www.sywx8.com/modules/article/search.php?searchkey=${$URL.encode(keywords)}`
}, 8000, "GBK");
for (let a of doc.querySelectorAll(".c_row .c_subject a")) {
// 这个网站比较特殊,链接默认是完整的
links.push({ "title": `(sywx) ${a.textContent}`, "href": attr(a, "href") });
}
return links;
}
},
{
"desc": "bqxs",
"url": "http://www.bqxs520.com/",
"main": ".box_con",
"title": "div.content_read h1",
"toc": "#list dd a",
"tocJump": 9,
"txt": "#content",
"filter": [".ywtop", ".header", ".nav", ".bottem1", ".lm", "#page_set", ".bookname~.box_con"],
"txtfilter": ["请记住本书", "http"],
"search": async (keywords, baseurl) => {
let links = [];
let doc = await rq({
"method": "POST",
"headers": { "Content-Type": "application/x-www-form-urlencoded" },
"url": encodeURI(`http://www.bqxs520.com/case.php?m=search`),
"data": `&key=${encodeURI(keywords)}`
}, 7000, "UTF-8");
for (let a of doc.querySelectorAll(".l .s2 a")) {
links.push({ "title": `(bqxs) ${a.textContent}`, "href": concatURL(baseurl, attr(a, "href")) });
}
return links;
}
},
{
"desc": "biqugetv",
"url": "https://www.biqugetv.com/",
"main": ".box_con",
"title": "div.content_read h1",
"toc": "#list dd a",
"tocJump": 0,
"txt": "#content",
"filter": [".ywtop", ".header", ".nav", ".bottem1", ".lm", "#page_set"],
"txtfilter": [],
"search": async (keywords, baseurl) => {
let links = [];
let doc = await rq({
"url": encodeURI(`https://www.biqugetv.com/search.php?keyword=${keywords}`)
}, 6000, "UTF-8");
for (let a of doc.querySelectorAll("h3 a")) {
links.push({ "title": `(biqugetv) ${a.textContent}`, "href": concatURL(baseurl, attr(a, "href")) });
}
return links;
}
},
{
"desc": "dshfood",
"url": "https://www.dshfood.net/",
"main": ".box_con",
"title": "div.content_read h1",
"toc": "#list dd a",
"tocJump": 9,
"txt": "#content",
"filter": [".ywtop", ".header", ".nav", ".bottem1", "#page_set", "#content>div"],
"txtfilter": ["笔趣阁"],
"nodash": true,
"funcFilter": () => document.querySelectorAll("img")
.forEach(e => { var _a; return (_a = e.parentElement) === null || _a === void 0 ? void 0 : _a.remove(); }),
"search": async (keywords, baseurl) => {
let links = [];
let doc = await rq({
"method": "POST",
"headers": {
"Content-Type": "application/x-www-form-urlencoded",
"referer": "https://www.dshfood.net/so/"
},
"url": "https://www.dshfood.net/so/",
// 鉴于使用了 GBK 进行编码,不能再使用 URLSearchParams
"data": `?searchtype=articlename&searchkey=${$URL.encode(keywords)}&submit=`
}, 6000, "GBK");
for (let a of doc.querySelectorAll(".line a.blue")) {
links.push({ "title": `(dshfood) ${a.textContent}`, "href": concatURL(baseurl, attr(a, "href")) });
}
return links;
}
}
],
"states": {
"fontSize": 16,
"lineHeight": 16 * lineHeight,
"toc": false,
"flow": false
},
"style": `
body {
background-color: #EAEAEF !important;
}
.bqg.inject.win {
width: 55vw !important;
min-width: 600px;
border: 2px double gray !important;
border-radius: 8px;
}
.bqg.inject.txt {
font-family: Calibri,'${defaultFont}',serif!important;
background-color: #EAEAEF !important;
padding: 0.5em 1em !important;
margin: 0.5em auto !important;
width: auto !important;
white-space: pre-wrap;
}
.bqg.inject.title {
color: black;
background-color: #EAEAEF;
font-family: Calibri,'${defaultFont}',serif!important;
cursor: pointer !important;
}
.bqg.inject.title:hover {
color: #0258d8 !important;
}
.hq.inject.toc {
font-family: Calibri,sans-serif;
width: 275px;
position: fixed;
top: 30px;
left: 8px;
/*目录默认是关闭的*/
transform: translateX(-300px);
opacity: 0;
padding: 5px;
display: flex;
flex-flow: column;
box-shadow: #7b7b7b 5px 4px 5px;
transition-property: transform, box-shadow, opacity;
transition-duration: .5s;
transition-timing-function:cubic-bezier(0.35, 1.06, 0.83, 0.99);
background: rgb(246 246 246 / 60%);
backdrop-filter: blur(2px);
border-radius: 8px;
}
.hq.inject ul {
height: 280px;
width: 100%;
/*offsetTop 计算需要*/
position:relative;
overflow: auto;
}
.hq.inject ul li {
cursor: pointer;
margin: 2px;
width: 95%;
padding: 1px 4px;
font-size: 12px;
border-radius: 4px;
}
.hq.inject ul li:hover {
background: #0258d8;
color: #f6f6f6;
}
.hq.inject.toc>h3 {
font-size: 1.1rem;
font-weight: bold;
border-radius: 2px;
align-self: center;
cursor: pointer;
margin: 4px 0 8px 0;
}
.hq.inject.toc>h3:hover {
color: #ffa631 !important;
}
.hq.inject.search {
font-family: Calibri,sans-serif;
width: 275px;
position: fixed;
top: 30px;
padding: 5px;
display: flex;
flex-flow: column;
transition: right 0.5s cubic-bezier(0.35, 1.06, 0.83, 0.99);
background: rgb(246 246 246 / 60%);
border-radius: 8px;
}
.hq.inject.search input {
margin: 8px auto;
width: 95%;
}
`
};
// 查询已经保存的字体信息
let savedStates = localStorage.getItem("bqg_cfg");
// 检查是否存在已有设置且和当前版本相符
let states;
if (savedStates === null) {
states = C.states;
console.warn("当前状态已保存");
}
else {
let cfg = JSON.parse(savedStates);
let defaultStates = Object.keys(C.states);
let cfg_ = Object.keys(cfg);
let useSaved = true;
// 检查键是否匹配
if (defaultStates.length == cfg_.length) {
for (let key of Object.keys(cfg)) {
if (!defaultStates.includes(key)) {
useSaved = false;
break;
}
}
}
else {
useSaved = false;
}
if (useSaved) {
states = cfg;
}
else {
states = C.states;
console.warn("检测到版本变化,状态已重置");
}
}
// 检测当前的网址,应用对应的设置
let tmp = C.sites.filter(site => document.URL.includes(site.desc));
if (tmp.length == 0) {
console.warn("没有匹配的设置,脚本已终止!");
return;
}
let currentSite = tmp[0];
// 完成样式注入
GM_addStyle(C.style);
/**
* 保存交互式状态
*/
function saveStates() {
localStorage.setItem("bqg_cfg", JSON.stringify(states));
}
/**
* 上一章,同时移除所有 flow 拼接结果
*/
function prevChapter() {
var _a;
(_a = fd(document, "a", "上一")) === null || _a === void 0 ? void 0 : _a.click();
}
/**
* 下一章,同时移除所有 flow 拼接结果
*/
function nextChapter() {
var _a;
(_a = fd(document, "a", "下一")) === null || _a === void 0 ? void 0 : _a.click();
}
/**
* 异步,向下拼页
* 绑定到事件上时务必注意重复触发的情况
*/
async function concatNextCh() {
var _a;
let next = fd(document, "a", "下一");
let prev = fd(document, "a", "上一");
let currentText = fd(document, currentSite.txt);
try {
let doc = await rq({ url: next === null || next === void 0 ? void 0 : next.href });
let text = fd(doc, currentSite.txt);
// console.log(text.textContent)
// 更好的性能
currentText.insertAdjacentHTML("beforeend", "<br><hr style='border: unset;border-top: 1px solid gray; margin: ${states.lineHeight}px 0'>");
currentText.insertAdjacentText("beforeend", txtFilter((_a = text.innerText) !== null && _a !== void 0 ? _a : "文本过滤错误", /(?![a-zA-Z0-9!.'"])\s+/));
// /id/xxx_1.html -> /id/xxx_1
let href = attr(next, "href").replace(/\.html$/, "");
// 重新渲染目录,currentBookToc 不可能为 null
renderTOC(JSON.parse(currentBookToc), ul, href);
// 重设上一页和下一页按钮的链接
prev.href = fd(doc, "a", "上一").href;
next.href = fd(doc, "a", "下一").href;
}
catch (error) {
currentText.innerText = currentText.innerText.concat("\n\n\t获取下一页错误,上下滚动以重新获取");
}
}
// 目录切换
function switchToc(open) {
let toc = fd(document, ".hq.inject.toc");
if (open) {
toc.style.transform = "translateX(0)";
toc.style.opacity = "1";
toc.style.boxShadow = "box-shadow: #7b7b7b 5px 3px 4px 0px;";
states.toc = true;
}
else {
toc.style.transform = "translateX(-300px)";
toc.style.opacity = "0";
toc.style.boxShadow = "box-shadow: #7b7b7b 5px 2px 0px 0px;";
states.toc = false;
}
saveStates();
}
// 目录开关
function toggleToc() {
if (states.toc) {
switchToc(false);
}
else {
switchToc(true);
}
}
/**
* 根据 site 中的条件进行过滤,同时将缩进统一
*
* @param itxt 需要过滤的,innerText 通用性最好
* @param delim 默认的切分点,从网页解析得到的内容和 ajax 获取到的内容切分点不一致
* @returns 过滤后字符串
*/
function txtFilter(itxt, delim = /\n/g) {
var _a;
// innerText 相对于 textContent 保留了视觉上的换行(块的换行)
return (_a = itxt === null || itxt === void 0 ? void 0 : itxt.split(delim)) === null || _a === void 0 ? void 0 : _a.filter(line => {
if (/^\s*$/.test(line))
return false;
// 去除白行和包含的关键字
for (const keyword of currentSite.txtfilter) {
if (line.includes(keyword)) {
return false;
}
}
return true;
}).map(line => `${" ".repeat(2)}${line.trim()}`).join("\n\n");
}
if (states.flow) {
// 变相 throttle 一下不然顶不住
let loading = false;
document.onscroll = async (_) => {
if (!loading && chkBoundry(true, window.innerHeight * 0.75)) {
loading = true;
// 意思是上一次拼页完过1.5秒才允许继续拼页,避免在加载下一页时反复调用拼页函数
// 效果比固定延迟要稳定
await concatNextCh();
setTimeout(() => { loading = false; }, 1500);
}
};
}
// 对可变部分产生影响
let doInject = function () {
var _a;
// 执行元素过滤
currentSite.filter.forEach(filter => { var _a; return (_a = document.querySelectorAll(filter)) === null || _a === void 0 ? void 0 : _a.forEach(ele => ele.remove()); });
// 执行自定义过滤
if (currentSite.funcFilter) {
currentSite.funcFilter();
}
// 应用已经保存的状态
let textWin = fd(document, currentSite.txt);
textWin.setAttribute("style", `font-size:${states.fontSize}px;line-height:${states.lineHeight}px`);
textWin.classList.add("bqg", "inject", "txt");
// 执行文字过滤
textWin.textContent = txtFilter((_a = textWin.innerText) !== null && _a !== void 0 ? _a : "文本过滤错误");
let mainWin = fd(document, currentSite.main);
mainWin.classList.add("bqg", "inject", "win");
let title = fd(document, currentSite.title);
title.title = "点击显示目录";
title.classList.add("bqg", "inject", "title");
title.onclick = (ev) => {
toggleToc();
// 避免跳到上一章
// 比下面的更为具体,所以有效。
ev.stopPropagation();
};
// 阻止双击事件被捕获(双击会回到顶部)
document.body.ondblclick = (ev) => ev.stopImmediatePropagation();
document.body.onclick = (ev) => {
let root = document.documentElement;
let winHeight = window.innerHeight;
// 下半屏单击下滚,反之上滚
if (ev.clientY > root.clientHeight / 2) {
if (chkBoundry() && !states.flow)
nextChapter();
window.scrollBy({ top: (window.innerHeight - lineHeight) * 1 });
}
else {
if (chkBoundry(false)) {
prevChapter();
}
window.scrollBy({ top: (window.innerHeight - lineHeight) * -1 });
}
};
document.body.onkeydown = (ev) => {
switch (ev.key) {
case "-":
states.fontSize -= 2;
textWin.style.fontSize = `${states.fontSize}px`;
states.lineHeight = states.fontSize * lineHeight;
textWin.style.lineHeight = `${states.lineHeight}px`;
saveStates();
break;
case "=":
states.fontSize += 2;
textWin.style.fontSize = `${states.fontSize}px`;
states.lineHeight = states.fontSize * lineHeight;
textWin.style.lineHeight = `${states.lineHeight}px`;
saveStates();
break;
case "j":
if (chkBoundry() && !states.flow) {
nextChapter();
}
else {
window.scrollBy({ top: window.innerHeight - states.lineHeight });
}
break;
case "k":
// 考虑在 flow 模式下也允许上一章
if (chkBoundry(false) && !states.flow) {
prevChapter();
}
else {
window.scrollBy({ top: -1 * (window.innerHeight - states.lineHeight) });
}
break;
case "h":
prevChapter();
break;
case "l":
nextChapter();
break;
case "t":
toggleToc();
break;
case "s":
toggleSearch();
break;
case "f":
states.flow = !states.flow;
saveStates();
break;
default:
break;
}
};
};
// 先调用一次,后面是有变化时才会触发,避免有时无法起作用
doInject();
// 强力覆盖
new MutationObserver((_, ob) => {
doInject();
}).observe(document.body, { childList: true });
// 添加目录
let toc = document.createElement("div");
toc.className = "hq inject toc";
toc.onclick = ev => ev.stopPropagation();
// 已保存状态读取
document.body.append(toc);
if (states.toc)
switchToc(true);
// 目录状态指示灯
let pointer = document.createElement("h3");
// 当然也可以靠不同类名实现
let pointerColors = { "loaded": "#afdd22", "loading": "#ffa631", "unload": "#ed5736" };
pointer.title = "点击以重新加载目录";
pointer.innerHTML = "目<span style='display: inline-block;width: 1em'></span>录";
pointer.style.cursor = "pointer";
pointer.style.color = pointerColors.unload;
toc.append(pointer);
// 目录列表
let ul = document.createElement("ul");
toc.append(ul);
/**
* 从源渲染目录到指定元素
*
* @param toc 目录源
* @param ul 容器
* @param href 定位链接,格式 http://host/id/chp.html 中最短为 /id/chp 部分
*/
function renderTOC(toc, ul, href) {
var _a;
// 清空旧内容
ul.innerHTML = "";
let current = null;
// 进度计数器
let counter = 1;
for (let lnk of toc) {
let li = document.createElement("li");
li.textContent = lnk.title;
// 根据传入的 href 是否包含目录中的链接来判定,因为有的网站包含子页面 XXXX_1.html 形式
// 比对时标准目录链接 lnk: /id/chp.html 之中,仅取用 chp
let last = (_a = lnk.href.replace(".html", "")) !== null && _a !== void 0 ? _a : "";
if (current == null && href.includes(last)) {
li.innerHTML = `${lnk.title}<span style="flex: 1;"></span>${(counter / toc.length * 100).toFixed(1)}%`;
current = li;
}
li.onclick = (ev) => {
document.location.href = lnk.href;
ev.stopPropagation();
};
ul.append(li);
counter++;
}
// 渲染完修改指示灯状态
pointer.style.color = pointerColors.loaded;
// 滚动到当前位置,并高亮
if (current !== null) {
current.setAttribute("style", "display:flex;font-weight:bold;background: #0258d8;color: #f6f6f6;");
ul.scrollTo({ top: current.offsetTop - 130 });
}
}
/**
* 获取目录信息
*
* @param currentBookLink 当前书的链接,用作存储的键
* @param pointer 指示灯,在需要的时候修改状态
*/
async function fetchTOC(currentBookLink, pointer) {
var _a;
// 修改指示灯状态
pointer.style.color = pointerColors.loading;
try {
let doc = await rq({ url: currentBookLink });
let tocs = doc.querySelectorAll(currentSite.toc);
let data = [];
// 序列化存储准备
for (let link of tocs) {
// 使用字面意义上的链接 /chapter.html 而不是 http://**/id/chapter.html 以减小存储量
data.push({ "title": (_a = link.textContent) !== null && _a !== void 0 ? _a : "", "href": attr(link, "href") });
}
if (currentSite.tocJump)
data = data.slice(currentSite.tocJump);
// 缓存目录信息
let stdata = JSON.stringify(data);
sessionStorage.setItem(currentBookLink, stdata);
// 更新变量,避免章节拼接时以为找不到
currentBookToc = stdata;
renderTOC(data, ul, href);
}
catch (_) {
pointer.style.color = pointerColors.unload;
}
}
let source = document.URL.split("/");
source.pop();
// 用来定位的 url
let href = document.URL.replace(/\.html$/, "");
// 最后加斜杠保险
let currentBook = source.join("/");
if (!currentSite.nodash) {
currentBook += "/";
}
let currentBookToc = sessionStorage.getItem(currentBook);
if (currentBookToc === null) {
fetchTOC(currentBook, pointer);
}
else {
renderTOC(JSON.parse(currentBookToc), ul, href);
}
// 单击指示灯刷新目录缓存
pointer.onclick = _ => fetchTOC(currentBook, pointer);
// 添加聚合搜索
let searchBox = document.createElement("div");
searchBox.onclick = ev => ev.stopPropagation();
searchBox.onkeydown = ev => ev.stopPropagation();
searchBox.className = "hq inject search";
searchBox.style.right = "-300px";
searchBox.innerHTML = `
<input id="insearch" type="search" placeholder="至少输入两个字"/>
<span style="align-self:center;margin-bottem: 4px;color: ${pointerColors.loaded}">已就绪</span>
`;
document.body.append(searchBox);
let inputBox = fd(searchBox, "#insearch");
let search_ul = document.createElement("ul");
searchBox.append(search_ul);
let search_pointer = fd(searchBox, "#insearch~span");
// debounce 一下不然顶不住
let timer = null;
inputBox.oninput = _ => {
if (timer !== null)
clearTimeout(timer);
timer = setTimeout(async () => {
var _a, _b;
// 放外面也可
if (((_a = inputBox) === null || _a === void 0 ? void 0 : _a.value.length) < 2)
return;
// 更新指示灯
search_pointer.textContent = `正在搜索:${inputBox.value}`;
search_pointer.style.color = pointerColors.loading;
let requests = [];
let others = [{ "title": "没有搜索结果,也可以看看:", "href": "#" }];
for (let s of C.sites) {
if (s.search !== undefined) {
// 搜索开始
requests.push(s.search((_b = inputBox) === null || _b === void 0 ? void 0 : _b.value, s.url));
}
else {
others.push({ "title": s.desc, "href": s.url });
}
}
let result_count = 0, failed = 0;
let list = await Promise.allSettled(requests);
// 获取结果后清空旧内容
search_ul.innerHTML = "";
for (let site of list) {
if (site.status === "fulfilled") {
for (let lnk of site.value) {
let li = document.createElement("li");
li.textContent = lnk.title.trim();
li.onclick = ev => GM_openInTab(lnk.href, { active: true });
search_ul.append(li);
result_count++;
}
}
else {
failed++;
}
}
// 处理一下没有结果的情况,把没有实现 search 的网站摆上去
if (result_count === 0) {
for (let o of others) {
let li = document.createElement("li");
li.textContent = o.title;
li.onclick = ev => GM_openInTab(o.href, { active: true });
search_ul.append(li);
}
}
// 更新指示
search_pointer.textContent = `搜索完成:${result_count} 条结果 [${failed} 错误]`;
search_pointer.style.color = pointerColors.loaded;
}, 1000);
};
// 搜索框开关
function toggleSearch() {
if (parseInt(searchBox.style.right) < 0) {
searchBox.style.right = "8px";
}
else {
searchBox.style.right = "-300px";
}
}
/*
以下是工具函数
*/
/**
* 发起请求
*
* @param details 油猴标准请求格式,onload,onerror,responseType 会被忽略
* @param timeout 超时时间 默认:5000
* @param encoding 请求数据的编码 默认:当前所在页面的编码
* @returns Promise<Document>
*/
function rq(details, timeout = 5000, encoding) {
// 自动探测一手
if (!encoding)
encoding = document.characterSet;
return new Promise((res, rej) => {
details.onerror = rej;
details.ontimeout = rej;
details.timeout = timeout;
details.responseType = "arraybuffer";
details.onload = resp => {
if (resp.status != 200)
rej();
let decoder = new TextDecoder(encoding);
res(new DOMParser()
.parseFromString(decoder.decode(resp.response), "text/html"));
};
GM_xmlhttpRequest(details);
});
}
/**
* 返回符合条件的第一个元素
*
* @param doc 被查找的文档
* @param selector 选择器
* @param text 可选 元素的文本(子字符串)
* @returns 符合条件的元素
*/
function fd(doc, selector, text) {
var _a;
if (text) {
for (let e of doc.querySelectorAll(selector)) {
if ((_a = e.textContent) === null || _a === void 0 ? void 0 : _a.includes(text)) {
return e;
}
}
}
else {
return doc.querySelector(selector);
}
return null;
}
/**
* 拼接 URL
*
* @param host 网站域名
* @param path 一般是链接之中的相对路径
* @returns 完整的 URL
*/
function concatURL(host, path) {
let url = new URL(host);
url.pathname = path;
return url.toString();
}
/**
* 如果是 a 标签,且想要获取字面上的 href,必须使用此方法,不可以用 a.href
*
* @param ele 标签名
* @param attr 属性名
* @returns 属性值
*/
function attr(ele, attr) {
var _a;
return (_a = ele.getAttribute(attr)) !== null && _a !== void 0 ? _a : "";
}
/**
* 检查当前位置是否处于边界
*
* @param bottom 是否检查到达底部,否则检查是否处于顶部
* @param range 距离底部多少,默认是 0(最底部)
* @returns boolean
*/
function chkBoundry(bottom = true, range = 0) {
let root = document.documentElement;
let winHeight = window.innerHeight;
if (bottom) {
return (root.scrollTop + winHeight + range >= root.scrollHeight);
}
else {
return (root.scrollTop == 0);
}
}
})();