Greasy Fork is available in English.

accessibility_知乎键盘访问优化

针对知乎的屏幕阅读器可访问性优化

// ==UserScript==
// @name        accessibility_知乎键盘访问优化
// @namespace    https://www.zhihu.com/people/yin-xiao-bo-11
// @require    https://greasyfork.org/scripts/432103-az-%E5%BF%AB%E6%8D%B7%E9%94%AE/code/AZ%20%E5%BF%AB%E6%8D%B7%E9%94%AE.js?version=968636
// @version      0.7.1
// @description  针对知乎的屏幕阅读器可访问性优化
// @author       Veg
// @include        https://*.zhihu.com/*
// @include        https://zhuanlan.zhihu.com/*
// @grant        none
// ==/UserScript==
(function () {
'use strict';
let exclude = ':not(.veg-mark)';
let mo = new MutationObserver((mutationRecord) => {
middleFunction();
});
mo.observe(document.body, {
'childList': true,
'subtree': true
});
function middleFunction() {
globalShortcutKey();
middlewareFunction();
proc();
}
function middlewareFunction() {
let token = window.location.href.substring(20).split('/');
if (token[1] == 'follow' || token[1] == 'hot' || token[1] == '') {
let aside = document.querySelector('div.TopstoryPageHeader-aside' + exclude);
if (aside) {
aside.classList.add('veg-mark');
aside.parentNode.setAttribute('style', 'display:none');
}
}
if (token[1] == 'topic') {
//
}
if (token[1] == 'question') {
let title = document.querySelector('h1.QuestionHeader-title');
if (title) {
title.setAttribute('style', 'display:none');
}
let expandable = document.querySelector('div.QuestionRichText');
if (expandable) {
expandable.setAttribute('tabindex', '-1');
let more = expandable.querySelector('button.QuestionRichText-more');
if (more) {
more.addEventListener('click', () => {
expandable.focus();
}, null);
}
}
}
if (token[1] == 'people' || token[1] == 'org') {
let profile = document.querySelector('ul.ProfileMain-tabs');
if (profile) {
profile.setAttribute('style', 'display:none');
}
}
if (token[3] === 'log' || token[3] === 'logs' || token[3] === 'topic_logs') {
insdel();
}
//endFunction
}
//内容快捷键
function globalShortcutKey() {
let timelineStr = 'div.TopstoryItem' + exclude + ', div.QuestionAnswer-content' + exclude + ', div.List-item' + exclude + ', section.HotItem' + exclude + ', div.TopstoryItem-isFollow' + exclude;
let timeline = document.querySelectorAll(timelineStr);
for (let i = 0, l = timeline.length; i < l; i++) {
timeline[i].classList.add('veg-mark');
if (!timeline[i].classList.contains('hotkey-AZ') && timeline[i].hasAttribute('tabindex')) {
timeline[i].classList.add('hotkey-AZ');
}
let modalWrap = timeline[i].querySelector('div.ModalWrap');
if (modalWrap) {
modalWrap.style = 'display:none';
}
}
// end
}
//细节优化
function proc() {
let videoPackage = document.querySelectorAll('div.ZVideo-video' + exclude + ', div.VideoCard-player' + exclude);
for (let i = 0, l = videoPackage.length; i < l; i++) {
if (!videoPackage[i].classList.contains('veg-mark')) {
videoPackage[i].classList.add('veg-mark');
let button = document.createElement('button');
button.innerHTML = '播放';
button.style = 'background:#FFA500;';
button.addEventListener('click', function () {
let iframe = this.parentNode.querySelector('iframe');
if (iframe)
window.open(iframe.src);
}, null);
videoPackage[i].insertBefore(button, videoPackage[i].firstChild);
}
}
//选项卡
let tab = document.querySelectorAll('[role="tab"]' + exclude + ', [role="tablist"]' + exclude);
for (let i = 0, l = tab.length; i < l; i++) {
tab[i].classList.add('veg-mark');
tab[i].removeAttribute('role');
}
let tooltip = document.querySelectorAll('[data-tooltip]' + exclude);
for (let i = 0, l = tooltip.length; i < l; i++) {
tooltip[i].classList.add('veg-mark');
let label = tooltip[i].getAttribute('data-tooltip');
tooltip[i].setAttribute('aria-label', label);
}
//搜索
let search = document.querySelector('label.SearchBar-input' + exclude);
if (search) {
search.classList.add('veg-mark')
search.querySelector('input[placeholder]').setAttribute('aria-label', '知乎搜索');
}
//优化一些菜单项
let hdxx = document.querySelectorAll('button[aria-haspopup]' + exclude);
for (let i = 0, l = hdxx.length; i < l; i++) {
hdxx[i].classList.add('veg-mark');
hdxx[i].removeAttribute('role');
hdxx[i].removeAttribute('aria-haspopup');
}
/*
//补货通知
let notice = document.querySelector('div.Notification-textSection' + exclude);
if (notice) {
notice.classList.add('veg-mark')
let text = notice.innerText;
chrome.runtime.sendMessage({
ttsState: "speak",
text: text,
enqueue: false
});
}
*/
//公共编辑理由、上传视频、上传文档
//注册、登录
//发视频、写想法
let lyStr = 'div.css-stef5c' + exclude + ', div.SignFlow-tab' + exclude + ', div.Login-socialButton' + exclude + ', div.Editable-docModal-uploader-text' + exclude + ',div.Editable-videoModal-uploader-text' + exclude + ', div.NewGlobalWrite-topTitle' + exclude;
let ly = document.querySelectorAll(lyStr);
for (let i = 0, l = ly.length; i < l; i++) {
ly[i].classList.add('veg-mark')
ly[i].classList.add('zhihu-click');
ly[i].setAttribute('role', 'button');
ly[i].setAttribute('tabindex', '0');
}

// 隐藏选择语言、用户头像、匿名用户头像、动态中的原点
let hiddenElementStr = 'span.Footer-dot' + exclude + ', img[alt="匿名用户"]' + exclude + ', a.UserLink-link' + exclude + ', input[placeholder="选择语言"]' + exclude + ', span.Bull' + exclude;
let hiddenElement = document.querySelectorAll(hiddenElementStr);
for (let i = 0, l = hiddenElement.length; i < l; i++) {
hiddenElement[i].classList.add('veg-mark')
if (hiddenElement[i].hasAttribute('alt') ||
hiddenElement[i].hasAttribute('placeholder') ||
hiddenElement[i].querySelector('img[src]') ||
hiddenElement[i].classList.contains('Bull') ||
hiddenElement[i].classList.contains('Footer-dot')
) {
hiddenElement[i].setAttribute('style', 'display:none');
}
}
// 优化对话框访问
(function () {
setTimeout(function () {
let dhk = document.querySelectorAll("div.Modal" + exclude);
for (let i = 0, l = dhk.length; i < l; i++) {
dhk[i].classList.add('veg-mark')
dhk[i].setAttribute("role", "dialog");
dhk[i].setAttribute("aria-labelledby", ":1");
let dhks = dhk[i].querySelectorAll("h3.Modal-title,div.Topbar-title,.CommentTopbar-title");
for (let i = 0, l = dhks.length; i < l; i++) {
dhks[i].setAttribute("id", ":1");
}
let yc = dhk[i].querySelectorAll("button.Tag-remove");
for (let i = 0, l = yc.length; i < l; i++) {
yc[i].setAttribute('aria-label', '移除');
}
}
}, 80);
})();
//给匿名用户文本增加焦点
let users = document.querySelectorAll('span.UserLink' + exclude);
for (let i = 0; i < users.length; i++) {
users[i].classList.add('veg-mark')
let names = users[i].innerText;
if (names == '匿名用户') {
users[i].setAttribute('tabindex', '0');
users[i].setAttribute('role', 'link');
}
}
//处理评论
let plButton = document.querySelectorAll('div.CommentItem-footer' + exclude + ',div.CommentItemV2-footer' + exclude);
for (let i = 0, l = plButton.length; i < l; i++) {
plButton[i].classList.add('veg-mark')
plButton[i].setAttribute('tabindex', '-1');
plButton[i].setAttribute('role', 'link');
}
// endFunction
}
document.body.addEventListener("keydown", function (k) {
shareShortcutKey(k);
//键盘点击
let t = k.target;
if (t.classList.contains('hotkey-AZ')) {
if (k.altKey && k.keyCode == 13) {
let text = ClearBr(t.innerText);
navigator.clipboard.writeText(text);
}
}
if (t.classList.contains('zhihu-click')) {
if (k.keyCode == 13 || k.keyCode == 32) {
t.click();
}
}
}, null);
// 阅读全文和收起的焦点管理
document.body.addEventListener('click', (e) => {
let t = e.target;
if (t.classList.contains('ContentItem-more') || t.classList.contains('ContentItem-expandButton') || t.classList.contains('ContentItem-action')) {
let parent = t.parentNode;
while (!parent.classList.contains('hotkey-AZ')) {
parent = parent.parentNode;
}
if (parent.classList.contains('hotkey-AZ')) {
let heading = parent.querySelector('h2.ContentItem-title');
if (heading) {
heading.querySelector('a').focus();
}
else {
parent.focus();
}
}
}
}, null);
//全局快捷键函数
function shareShortcutKey(k) {
let hotkey = document.querySelectorAll('.hotkey-AZ');
for (let i=0, l=hotkey.length; i<l; i++) {
if (k.altKey && k.keyCode == 65) {
hotkey[0].focus();
}
if (k.altKey && k.keyCode == 90) {
hotkey[l - 1].focus();
}
}
//获取页面代码
if (k.altKey && k.keyCode == 66) {
console.log(document.body.innerHTML);
var input = document.createElement('input');
input.value = document.body.innerHTML;
document.body.appendChild(input);
}
if (k.altKey && k.keyCode == 82) {
document.querySelector('button[aria-label="更多"]').focus();
}
if (k.keyCode == 113) {
let Comment = document.querySelector('div.CommentItemV2');
if (Comment !== null) {
Comment.setAttribute('tabindex', '-1');
Comment.focus();
} else {
let pinComment = document.querySelector('div.CommentItem');
if (pinComment !== null)
pinComment.setAttribute('tabindex', '-1');
pinComment.focus();
}
}
if (k.altKey && k.keyCode == 81) {
var gb = document.querySelectorAll("button.ContentItem-action");
for (var i = 0; i < gb.length; i++) {
var gbName = gb[i].innerText;
var gbNames = gbName.substring(2, 6);
if (gbName == '​收起评论' || gbNames == '收起评论') {
gb[i].click();
gb[i].focus();
}
}
}
if (k.altKey && k.keyCode == 49) {
var f = document.querySelectorAll('a.QuestionMainAction,a.NumberBoard-item,a[href="/lives"],button.follow-button,button.NumberBoard-itemWrapper');
for (var i = 0; i < f.length; i++) {
//a.zg-btn-white,
f[0].focus();
}
}
if (k.altKey && k.keyCode == 50) {
document.querySelector("button.PaginationButton-prev").focus();
}
if (k.altKey && k.keyCode == 51) {
document.querySelector("button.PaginationButton-next").focus();
}
if (k.altKey && k.keyCode == 52) {
var more = document.querySelector('a.zu-button-more'); {
more.setAttribute('tabindex', '0');
more.focus();
}
}
if (k.ctrlKey && k.keyCode == 81) {
document.querySelector('a[href="/community"]').focus();
}
if (k.altKey && k.keyCode == 88) {
if (document.querySelector('button.QuestionHeader-edit') !== null) {
document.querySelector('button.QuestionHeader-edit').focus();
} else {
document.querySelector('button.NumberBoard-item').focus();
}
}
}

function insdel() {
let ins = document.querySelectorAll('ins' + exclude);
for (let i = 0, l = ins.length; i < l; i++) {
if (!ins[i].classList.contains('veg-mark')) {
ins[i].classList.add('veg-mark');
let parent = ins[i].parentNode;
let div1 = document.createElement("div");
div1.innerHTML = "「已插入:";
parent.insertBefore(div1, ins[i]);
div1.appendChild(ins[i]);
let div2 = document.createElement("span");
div2.innerHTML = ":插入结束」";
ins[i].appendChild(div2);
}
}
let del = document.querySelectorAll('del' + exclude);
for (let i = 0, l = del.length; i < l; i++) {
if (!del[i].classList.contains('veg-mark')) {
del[i].classList.add('veg-mark');
let parent = del[i].parentNode;
let div1 = document.createElement("div");
div1.innerHTML = "「已删除:";
parent.insertBefore(div1, del[i]);
div1.appendChild(del[i]);
let div2 = document.createElement("span");
div2.innerHTML = ":删除结束」";
del[i].appendChild(div2);
}
}
let fh = document.querySelectorAll('span.zg-bull' + exclude);
for (let i = 0, l = fh.length; i < l; i++) {
fh[i].classList.add('veg-mark');
let name = fh[i].innerText;
if (name == '•') {
fh[i].setAttribute('style', 'display:none');
}
}
let gly = document.querySelectorAll('a' + exclude);
for (let i = 0; i < gly.length; i++) {
gly[i].classList.add('veg-mark');
let name = gly[i].innerText;
if (name == '知乎管理员') {
gly[i].setAttribute('href', 'javascript:;');
}
}
}
function ClearBr(key) {
key = key.replace(
/\n(\n)*( )*(\n)*\n/g,
"\n");
return key;
}
//判断是否有可被 focus 的能力
function abilityDetection(t) {
if ((t.tabIndex >= 0 || t.hasAttribute && t.hasAttribute('tabindex') && t.getAttribute('tabindex') == '-1') &&
!t.hasAttribute('disabled') &&
t.getAttribute('aria-hidden') !== 'true' &&
t.offsetParent !== null)
return true;
}
})();