// ==UserScript==
// @name 知乎手机网页版改进
// @namespace https://www.zhihu.com/
// @match https://www.zhihu.com/question/*
// @grant none
// @version 1.2.6
// @author nameldk
// @description 使手机网页版可以加载更多答案
// @note 2020.12.22 v1.2.6 修复链接无法打开的问题,外部链接直接打开
// @note 2020.10.13 v1.2.5 修复蒙层偶尔不消失的问题
// @note 2020.09.14 v1.2.4 修复评论超出的问题
// @note 2020.08.14 v1.2.3 适配新版页面
// @note 2020.08.13 v1.2.2 修复已加载完的评论切换排序不显示的问题
// @note 2020.08.03 v1.2.1 处理评论加载不完全,评论作者标识,收起按钮颜色区分,一些样式调整
// @note 2020.08.02 v1.2 处理gif,视频,收起后的定位,发布时间,页面被清空的问题
// @downloadURL none
// ==/UserScript==
const questionNumber = (location.href.match(/\/question\/(\d+)/)||[])[1];
const inDetailPage = location.href.match(/\/question\/\d+\/answer\/\d+/);
const fromMobile = navigator.userAgent.match(/Android|iPhone|iPad|iPod|Opera Mini|IEMobile/i);
var offset = 0;
var limit = 5;
var is_end = 0;
var elList = null;
var elLoading = null;
var loadAnswerInterval = null;
var loadCommentInterval = null;
var viewportElCheckList = [];
var debug = 0;
var log = debug ? console.log : function(){};
function forEachArray(arrayLike, cb) {
if (arrayLike) {
Array.prototype.forEach.call(arrayLike, el => cb(el));
}
}
function forEachBySelector(s, cb) {
Array.prototype.forEach.call(document.querySelectorAll(s), el => cb(el));
}
function removeBySelector(s) {
forEachBySelector(s, ele => ele.remove());
}
function hideBySelector(s) {
forEachBySelector(s, ele => ele.style.display = "none");
}
function getElementHeight(el) {
if (el) {
// el.offsetHeight
return parseFloat(window.getComputedStyle(el, null).height.replace("px", ""));
}
return 0;
}
function isElementInViewport (el) {
// https://stackoverflow.com/questions/123999/how-can-i-tell-if-a-dom-element-is-visible-in-the-current-viewport
if (!el)
return false;
var rect = el.getBoundingClientRect();
if (rect.top >= 0) { // ↓
return rect.top < window.innerHeight;
} else {
return rect.top + rect.height > 0;
}
}
function formatNumber(num) {
if (num > 10000) {
return (num / 10000).toFixed(2) + '万';
} else {
return num;
}
}
function formatUrl(url, formatStr) {
if (!formatStr)
formatStr = 'xs';
// s,xs,m, r
return url.replace('{size}', formatStr);
}
function formatDate(e, t) {
if(e.toString().length === 10) { // 秒
e = e*1000;
}
e = new Date(e);
// yyyy-MM-dd hh:mm:ss
var n = {
"M+": e.getMonth() + 1,
"d+": e.getDate(),
"h+": e.getHours(),
"m+": e.getMinutes(),
"s+": e.getSeconds(),
"q+": Math.floor((e.getMonth() + 3) / 3),
S: e.getMilliseconds()
};
/(y+)/.test(t) && (t = t.replace(RegExp.$1, (e.getFullYear() + "").substr(4 - RegExp.$1.length)));
for (var r in n)
new RegExp("(" + r + ")").test(t) && (t = t.replace(RegExp.$1, 1 === RegExp.$1.length ? n[r] : ("00" + n[r]).substr(("" + n[r]).length)));
return t
}
function getDate(timestamp) {
return formatDate(timestamp, 'yyyy-MM-dd');
}
// ---biz---
function skipOpenApp() {
log('run:skipOpenApp');
// .ContentItem.AnswerItem
// .RichContent.is-collapsed.RichContent--unescapable
Array.prototype.forEach.call(document.querySelectorAll('.ContentItem.AnswerItem'), function (ele) {
let elRichContentInner = ele.querySelector('.RichContent-inner');
let button = ele.querySelector('button');
if (button) {
button.style.display = 'none';
}
if (elRichContentInner) {
let elMTimeMeta = ele.querySelector('meta[itemprop="dateModified"]');
let elCTimeMeta = ele.querySelector('meta[itemprop="dateCreated"]');
if (elMTimeMeta && elCTimeMeta) {
let mTime = elMTimeMeta.getAttribute('content').toString().split('T')[0];
let cTime = elCTimeMeta.getAttribute('content').toString().split('T')[0];
let elATime = elRichContentInner.parentElement.querySelector('.ContentItem-time');
let url = elCTimeMeta.previousElementSibling.getAttribute('content');
let mHtml = '';
if (mTime !== cTime) {
mHtml = `编辑于 ${mTime}`;
}
let tmpHtml = `
`;
if (elATime) {
elATime.remove();
}
elRichContentInner.insertAdjacentHTML('afterend', tmpHtml);
}
if (elRichContentInner.parentElement.classList.contains('is-collapsed')) {
ele.classList.add('my-fold');
setTimeout(function () {
elRichContentInner.insertAdjacentHTML('afterend', `↓展开↓↑收起↑`);
elRichContentInner.parentElement.classList.remove('is-collapsed');
elRichContentInner.setAttribute("style", "");
processFold(elRichContentInner.parentElement);
}, 1000);
}
forEachArray(elRichContentInner.querySelectorAll('.GifPlayer'), el => {
el.addEventListener('click', () => {
let elImg = el.querySelector('img'),
elIcon = el.querySelector('svg'),
url = elImg.getAttribute('src').toString().replace('.jpg', '.webp');
if (elIcon) {
elImg.setAttribute('src', url);
elIcon.remove();
}
});
});
}
ele.addEventListener("click", function (event) {
event.preventDefault();
event.stopPropagation();
if (elRichContentInner) {
elRichContentInner.setAttribute("style", "");
}
});
bindClickComment(ele.parentElement);
});
document.body.classList.remove('ModalWrap-body');
document.body.style.overflow = "auto";
removeBySelector('div.Card.AnswersNavWrapper div.ModalWrap');
}
function removeAds() {
log('run:removeAds');
Array.prototype.forEach.call(document.querySelectorAll('.MBannerAd'), function (ele) {
ele.parentNode.removeChild(ele)
});
}
function removeBlock() {
log('run:removeBlock');
removeBySelector('.MobileModal-backdrop');
removeBySelector('.MobileModal--plain.ConfirmModal');
removeBySelector('.AdBelowMoreAnswers');
removeBySelector('div.Card.HotQuestions');
removeBySelector('button.OpenInAppButton.OpenInApp');
removeBySelector('.CommentsForOia');
hideBySelector('div.ModalWrap');
let counter = 3;
let interval = null;
interval = setInterval(function () {
forEachBySelector('iframe', ele => {
if (ele.getAttribute('src').indexOf('https://www.zhihu.com/') !== 0) {
ele.remove();
}
});
counter--;
if (counter < 0) {
clearInterval(interval);
}
}, 1000);
}
function processContent(content) {
if (!content)
return '';
var r = /
/g;
return content.replace(r, '
');
}
function loadContent(offset, limit) {
var url = `https://www.zhihu.com/api/v4/questions/${questionNumber}/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Crelevant_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cis_labeled%2Cis_recognized%2Cpaid_info%2Cpaid_info_content%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%2A%5D.topics&limit=${limit}&offset=${offset}&platform=desktop&sort_by=default`;
return fetch(url).then(response => response.json());
}
function genAnswerItemHtml(data) {
var content = processContent(data.content);
let upTimeHtml = '';
if (getDate(data.created_time) !== getDate(data.updated_time)) {
upTimeHtml = `编辑于 ${formatDate(data.updated_time, 'yyyy-MM-dd')}`;
}
var html = `
${content}
↓展开↓
↑收起↑
`;
return html;
}
function genVideoHtml(videoId) {
if (!videoId)
return '';
var html = `
`;
return html;
}
function processVideo(elAncestor) {
if (elAncestor) {
forEachArray(elAncestor.querySelectorAll('a.video-box'), el => {
let videoId = el.dataset.lensId;
if (videoId) {
let html = genVideoHtml(videoId);
let div = document.createElement('div');
div.innerHTML = html;
el.insertAdjacentElement('afterend', div);
el.parentElement.removeChild(el);
}
});
}
}
function getListWrap() {
if (!elList) {
elList = document.querySelectorAll('.Question-main .List');
if (elList)
elList = elList[elList.length - 1];
}
return elList;
}
function loadAnswer() {
if (is_end) {
return;
}
if (elLoading) {
elLoading.classList.remove('hide');
}
loadContent(offset, limit).then(function (data) {
if (elLoading) {
elLoading.classList.add('hide');
}
log('get data:', offset, limit);
if (data.paging.is_end) {
is_end = 1;
}
offset += data.data.length;
let elListWrap = getListWrap();
if (elListWrap) {
data.data.forEach(function (item) {
let elListItemWrap = document.createElement('div');
elListItemWrap.innerHTML = genAnswerItemHtml(item);
elListWrap.insertAdjacentElement("beforeend", elListItemWrap);
processFold(elListItemWrap.querySelector('.RichContent'));
bindClickComment(elListItemWrap);
processAHref(elListItemWrap);
processVideo(elListItemWrap);
});
if (is_end) {
let html = '全部回答已加载完成...
'
elListWrap.insertAdjacentHTML("beforeend", html);
}
} else {
console.warn('elListWrap empty');
}
})
}
function addViewportCheckList(elListItem) {
if (elListItem) {
viewportElCheckList.push(elListItem);
}
}
function removeViewportCheckList(elListItem) {
viewportElCheckList.forEach(function (v, i) {
if (v === elListItem) {
viewportElCheckList.splice(i, 1);
}
})
}
function processFold(elRichContent) {
var elMoreBtn = elRichContent.querySelector('.my-more-btn');
var elLessBtn = elRichContent.querySelector('.my-less-btn');
var elContentItem = elLessBtn.closest('.ContentItem');
if (elMoreBtn && elLessBtn && elContentItem) {
let height = getElementHeight(elRichContent);
if (height > 0 && height < 400 && elRichContent.querySelectorAll('img').length < 2) {
elContentItem.classList.remove('my-fold');
elMoreBtn.remove();
elLessBtn.remove();
} else {
elMoreBtn.addEventListener('click', function (e) {
elContentItem.classList.add('my-unfold');
elContentItem.classList.remove('my-fold');
addViewportCheckList(elContentItem);
});
elLessBtn.addEventListener('click', function (e) {
elContentItem.classList.add('my-fold');
elContentItem.classList.remove('my-unfold');
removeViewportCheckList(elContentItem);
window.scrollTo(0, elContentItem.closest('.List-item').offsetTop);
});
}
}
}
function bindLoadData() {
log('run:bindLoadData');
var el = document.querySelector('div.Card.ViewAllInappCard');
if (inDetailPage) {
el.style.textAlign = "center";
el.innerHTML = '查看所有回答';
return;
}
el.insertAdjacentHTML('beforebegin', ``);
elLoading = document.getElementById('my-loading');
window.onscroll = function() {
if (is_end) {
return;
}
if ((window.innerHeight + window.scrollY + 100) >= document.body.offsetHeight) {
log('reach bottom');
if (loadAnswerInterval) {
clearTimeout(loadAnswerInterval);
}
loadAnswerInterval = setTimeout(function(){
log('to load', offset, limit);
loadAnswer();
}, 100);
}
};
}
function bindProcessViewport() {
log('run:bindProcessViewport');
var interval;
document.addEventListener('scroll', function () {
if (interval) {
clearTimeout(interval);
}
interval = setTimeout(function () {
// log('scroll-view:', viewportElCheckList.length);
if (viewportElCheckList.length) {
viewportElCheckList.forEach(function (elListItem) {
var elLessBtn = elListItem.querySelector('.my-less-btn');
if (isElementInViewport(elListItem)) {
elLessBtn.classList.remove('hide');
} else {
elLessBtn.classList.add('hide');
}
});
}
}, 100);
}, false);
}
function loadCommentData(answerId, offset, isReverse) {
if (!answerId) {
return;
}
let url = `https://www.zhihu.com/api/v4/answers/${answerId}/root_comments?limit=10&offset=${offset}&order=normal&status=open`;
if (isReverse)
url = `https://www.zhihu.com/api/v4/answers/${answerId}/comments?limit=10&offset=${offset}&order=reverse&status=open`;
return fetch(url).then(response => response.json());
}
function bindClickComment(elListItem) {
if (!elListItem)
return;
let elButton = elListItem.querySelector('button.ContentItem-action.Button--withLabel');
let elComment = elListItem.querySelector('.Comments-container');
elButton.addEventListener('click', function () {
if (elComment) {
elComment.classList.toggle('hide');
} else {
let answerId = (elListItem.querySelector('.ContentItem-meta ~ meta[itemprop="url"]').getAttribute('content').match(/\/answer\/(\d+)/) || [])[1];
elComment = addCommentWrap(elListItem, answerId);
let elCommentWrap = elComment.querySelector('.CommentListV2');
let elSwitchBtn = elComment.querySelector('div.Topbar-options > button');
let elCommentFold = elComment.querySelector('a.comment-fold');
elComment.dataset.answerId = answerId;
elComment.dataset.offset = "0";
processComment(elComment, elCommentWrap);
elCommentWrap.addEventListener('scroll', function(){
if (elCommentWrap.scrollTop + elCommentWrap.offsetHeight + 100 > elCommentWrap.scrollHeight) {
processComment(elComment, elCommentWrap);
}
}, false);
elSwitchBtn.addEventListener('click', function(){
if (elSwitchBtn.innerText === '切换为时间排序') {
elSwitchBtn.innerText = '切换为默认排序';
elComment.dataset.isReverse = "0";
} else {
elSwitchBtn.innerText = '切换为时间排序';
elComment.dataset.isReverse = "1";
}
elComment.dataset.offset = "0";
elComment.dataset.isEnd = "0";
elCommentWrap.innerHTML = '';
processComment(elComment, elCommentWrap);
});
elCommentFold.addEventListener('click', function(){
elComment.classList.add('hide');
});
}
});
}
function addCommentWrap(elListItem, answerId) {
if (!elListItem)
return;
var commentCount = elListItem.querySelector('meta[itemprop="commentCount"]').getAttribute('content');
let html = ``;
elListItem.insertAdjacentHTML("beforeend", html);
return elListItem.querySelector('.Comments-container');
}
function genCommentHtml(dataList) {
if (!dataList || !dataList.length)
return '';
let html = '';
dataList.forEach(function(data) {
html += genCommentItemHtml(data, data.child_comment_count, 0);
if (data.child_comment_count) {
data.child_comments.forEach(function (v) {
html += genCommentItemHtml(v, 0, 1);
})
}
});
if (html) {
return ``;
}
return html;
}
function genCommentItemHtml(item, hasChild, isChild) {
const liClass = !hasChild ? 'rootCommentNoChild' : (isChild ? 'child' : 'rootComment');
var replyHtml = '';
if (item.reply_to_author) {
if (item.author && item.author.role === 'author') {
replyHtml += ``;
}
replyHtml += `
${item.reply_to_author.member.name}
`;
}
var html = ``;
return html;
}
function genCommentLoding() {
var html = `
`;
var el = document.createElement('div');
el.innerHTML = html;
return el;
}
function processComment(elComment, elCommentWrap) {
if (!elComment || !elCommentWrap) {
return;
}
let offset = +elComment.dataset.offset,
answerId = elComment.dataset.answerId,
isReverse = +elComment.dataset.isReverse,
isEnd = +elComment.dataset.isEnd
;
if (loadCommentInterval) {
clearTimeout(loadCommentInterval);
}
if (!answerId || isEnd) {
return;
}
loadCommentInterval = setTimeout(function() {
log('beginLoadComment', offset);
var elLoading = genCommentLoding();
elCommentWrap.appendChild(elLoading);
loadCommentData(answerId, offset, isReverse).then(function (json) {
log('getCommentData', offset);
elComment.dataset.offset = offset + 10;
elCommentWrap.removeChild(elLoading);
elLoading = null;
let html = genCommentHtml(json.data);
if (json.paging.is_end) {
elComment.dataset.isEnd = "1";
html += '全部评论已加载完成...
'
}
elCommentWrap.insertAdjacentHTML('beforeend', html);
processAHref(elCommentWrap);
});
}, 100);
}
function processAHref(elAncestor) {
log('run:processAHref');
if (elAncestor) {
forEachArray(
elAncestor.querySelectorAll('a[href^="https://link.zhihu.com/"]'),
ele => {
ele.setAttribute('href', decodeURIComponent(ele.getAttribute('href').replace('https://link.zhihu.com/?target=', '')));
ele.setAttribute('target', '_blank');
ele.addEventListener('click', function (e) {
e.stopPropagation();
});
}
)
}
}
function processAllLink() {
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
processAHref(document);
const targetNode = document;
const config = { childList:true, subtree: true };
const callback = function(mutationsList) {
for(const mutation of mutationsList) {
if (mutation.addedNodes.length) {
forEachArray(mutation.addedNodes, ele => processAHref(ele));
}
}
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
function addCss() {
log('run:addCss');
var style = `
`;
document.body.insertAdjacentHTML('beforeend', style);
}
// init
if (fromMobile) {
setTimeout(function () {
addCss();
skipOpenApp();
bindLoadData();
bindProcessViewport();
}, 200);
setTimeout(function () {
removeAds();
removeBlock();
processAHref(document);
offset += document.querySelectorAll('.List-item').length;
}, 1000);
} else {
setTimeout(processAllLink, 500);
}
${commentCount} 条评论