// ==UserScript==
// @name accessibility_知乎键盘访问优化
// @namespace https://www.zhihu.com/people/yin-xiao-bo-11
// @version 0.4.1
// @description 针对知乎的屏幕阅读器可访问性优化
// @author Veg
// @include *.zhihu.com/*
// @grant none
// ==/UserScript==
(function () {
setTimeout(function () {
proc(document); amo(proc);
}, 10);
function proc(d) {
//选项卡
var tab = d.querySelectorAll('[role="tab"]');
for (var i = 0; i < tab.length; i++) {
tab[i].removeAttribute('role', '*');
}
//社交帐号登录(不可用)
var social = d.querySelectorAll('button.Login-socialButton');
for (var i = 0, l = social.length; i < l; i++) {
social[0].innerHTML = '微信登录(不可用)';
social[1].innerHTML = '微博登录(不可用)';
social[2].innerHTML = 'QQ 登录(不可用)';
}
//登录(不可用)和注册(不可用)的切换按钮
var title = document.title;
if (title == '知乎 - 发现更大的世界') {
var dl = document.querySelector('span[data-reactid="94"]'); {
dl.setAttribute('tabindex', '0');
dl.setAttribute('role', 'button');
dl.onkeydown = function (k) {
if (k.keyCode == 32 || k.keyCode == 13) {
this.click();
}
}
}
}
//工具栏提示
var toolbar = d.querySelectorAll("[data-tooltip]");
for (var i = 0, l = toolbar.length; i < l; i++) {
var text = toolbar[i].getAttribute("data-tooltip");
toolbar[i].setAttribute("aria-label", text);
if (text == "不感兴趣") {
toolbar[i].setAttribute("tabindex", "-1");
}
}
//优化一些菜单项
var hdxx = d.querySelectorAll("button[aria-expanded]");
for (var i = 0, l = hdxx.length; i < l; i++) {
//优化排序菜单
if (hdxx[i].classList.contains("Select-button")) {
hdxx[i].removeAttribute("role", "combobox");
}
hdxx[i].removeAttribute("aria-expanded", "*");
hdxx[i].removeAttribute("aria-haspopup", "*");
}
//给消息、私信、我增加名称
var name = d.querySelectorAll("button.PushNotifications-icon,button.Messages-icon,button.AppHeader-profileEntry")
for (var i = 0 , l = name.length; i < l; i++) {
var ad = name[i].lastChild;
if (ad.tagName == "SPAN")
break;
if (name[0].classList.contains("PushNotifications-icon")) {
var span = document.createElement("span");
span.innerHTML = " 消息";
name[0].appendChild(span);
}
if (name[1].classList.contains("Messages-icon")) {
var span = document.createElement("span");
span.innerHTML = " 私信";
name[1].appendChild(span);
}
name[2].setAttribute("aria-label", "我");
}
//隐藏一句话介绍
var grText = d.querySelectorAll('div.AuthorInfo-badgeText');
for (var i = 0, l = grText.length; i < l; i++) {
grText[i].setAttribute("aria-hidden", "true");
}
//优化对话框访问
(function () {
setTimeout(function () {
var dhk = d.querySelectorAll("div.Modal");
for (var i = 0; i < dhk.length; i++) {
dhk[i].setAttribute("role", "dialog");
dhk[i].setAttribute("aria-labelledby", ":1");
var dhks = dhk[i].querySelectorAll("h3.Modal-title,div.Topbar-title,.CommentTopbar-title");
for (var g = 0; g < dhks.length; g++) {
dhks[g].setAttribute("id", ":1");
}
}
}, 80);
})();
//给移除按钮增加名称
var yc = d.querySelectorAll("button.Tag-remove");
for (var i = 0, l = yc.length; i < l; i++) {
yc[i].setAttribute("aria-label", "移除");
}
//编辑问题
var edit = d.querySelectorAll('div.QuestionEdit-reason');
for (var i = 0, l = edit.length; i < l; i++) {
edit[i].setAttribute('tabindex', '0');
edit[i].setAttribute('role', 'button');
}
//删除选择语言
var delInput = d.querySelectorAll('input[placeholder="选择语言"]');
for (var i = 0; i < delInput.length; i++) {
var qn = delInput[i].parentNode;
var sc = qn.querySelector('input[placeholder="选择语言"]');
qn.removeChild(sc);
}
//删除头像
var tx = d.querySelectorAll('a.UserLink-link');
for (var i = 0, l = tx.length; i < l; i++) {
var qn = tx[i].parentNode;
var sc = qn.querySelector('a.UserLink-link');
var img = sc.querySelector('img');
if (img !== null) {
qn.removeChild(sc);
}
}
//删除匿名用户头像
var tx = d.querySelectorAll('img[alt="匿名用户"]');
for (var i = 0, l = tx.length; i < l; i++) {
var qn = tx[i].parentNode;
var sc = qn.querySelector('img[alt="匿名用户"]');
qn.removeChild(sc);
}
//处理评论
var plButton = d.querySelectorAll('div.CommentItem-footer');
for (var g = 0, l = plButton.length; g < l; g++) {
plButton[g].setAttribute('tabindex', '-1');
plButton[g].setAttribute('role', 'link');
}
//优化展开阅读全文后的键盘焦点
var zk = d.querySelectorAll("button.ContentItem-more,button.ContentItem-rightButton");
for (var i = 0, l = zk.length; i < l; i++) {
zk[i].addEventListener("click", focusA, null);
}
//优化收起后的键盘焦点
var sq = d.querySelectorAll("button.ContentItem-action");
for (var i = 0, l = sq.length; i < l; i++) {
sq[i].addEventListener("click", focusB, null);
}
//优化选中提示
var ts = d.querySelectorAll("button.AnswerItem-selectMenuItem,button.TopstoryItem-uninterestTag");
for (var i = 0, l = ts.length; i < l; i++) {
ts[i].setAttribute("role", "menuitemcheckbox");
if (ts[i].classList.contains("is-selected") || ts[i].classList.contains("is-active")) {
ts[i].setAttribute("aria-checked", "true");
}
}
//给图片增加 alt 属性
var img = document.querySelectorAll('img[src]');
for (var i = 0, l = img.length; i < l; i++) {
var alt = img[i].getAttribute('alt');
if (alt == null) {
img[i].setAttribute('alt', i + 1 + '图片');
}
}
var url = window.location.href;
var tokens = url.substring(20);
var token = tokens.split('/');
//隐藏首页重复内容
var yz = d.querySelectorAll("button.TopstorySideBar-navLink");
for (var i = 0, l = yz.length; i < l; i++) {
yz[i].setAttribute("tabindex", "-1");
}
if (token[1] == 'question') {
//隐藏问题页重复标题
document.querySelector("h1.QuestionHeader-title").setAttribute("aria-hidden", "true");
//回答页快捷键
var answer = document.querySelectorAll("div.AnswerItem");
for (var i = 0, l = answer.length; i < l; i++) {
answer[i].setAttribute('focuss', i);
var pq = answer[i].querySelector('p.p-serialNumber');
if (pq == null) {
var p = document.createElement("p");
p.innerHTML = i + 1;
p.className = 'p-serialNumber';
answer[i].insertBefore(p, answer[i].firstChild);
}
var tabindex = answer[i].getAttribute('tabindex');
if (tabindex == null) {
answer[i].setAttribute('tabindex', '-1');
answer[i].addEventListener("keydown", shortcutKey, null);
answer[i].addEventListener("keydown", function (k) {
if (k.target == this)
if (k.keyCode == 13) {
var name = this.innerText;
navigator.clipboard.writeText(name);
}
}, null);
}
}
}
if (token[1] !== 'question') {
//非问答页快捷键
var timeline = document.querySelectorAll('div.TopstoryItem,div.List-item');
for (var i = 0, l = timeline.length; i < l; i++) {
timeline[i].setAttribute('focuss', i);
var pq = timeline[i].querySelector('p.p-serialNumber');
if (pq == null) {
var p = document.createElement("p");
p.innerHTML = i + 1;
p.className = 'p-serialNumber';
timeline[i].insertBefore(p, timeline[i].firstChild);
}
var tabindex = timeline[i].getAttribute('tabindex');
if (tabindex == null) {
timeline[i].setAttribute('tabindex', '-1');
timeline[i].addEventListener("keydown", shortcutKey, null);
timeline[i].addEventListener("keydown", function (k) {
if (k.target == this)
if (k.keyCode == 13) {
var name = this.innerText;
navigator.clipboard.writeText(name);
}
}, null);
}
}
}
if (token[1] == 'people') {
//个人页重复内容
var div = document.querySelector("div.ProfileMain-header"); {
var x = div.querySelectorAll("ul,li,a,button");
for (var i = 0; i < x.length; i++) {
x[i].setAttribute("aria-hidden", "true");
x[i].setAttribute("tabindex", "-1");
}
}
}
//优化赞同按钮
var zt = d.querySelectorAll("button.VoteButton--up");
for (var i = 0, l = zt.length; i < l; i++) {
zt[i].removeAttribute("aria-label", "*");
var ad = zt[i].querySelector('span');
if (ad == null) {
var span = document.createElement("span");
if (zt[i].classList.contains("is-active")) {
span.innerHTML = " 已赞同";
zt[i].appendChild(span);
}
else {
span.innerHTML = " 未赞同";
zt[i].appendChild(span);
}
}
}
//优化反对按钮
var fd = d.querySelectorAll("button.VoteButton--down");
for (var i = 0, l = fd.length; i < l; i++) {
fd[i].removeAttribute("aria-label", "*");
var ad = fd[i].querySelector('span');
if (ad == null) {
var span = document.createElement("span");
if (fd[i].classList.contains("is-active")) {
span.innerHTML = "已反对";
fd[i].appendChild(span);
}
else {
span.innerHTML = "未反对";
fd[i].appendChild(span);
}
}
}
//优化赞按钮
var zan = d.querySelectorAll("button.LikeButton,button.CommentItem-likeBtn,button.PostIndex-voteButton");
for (var i = 0, l = zan.length; i < l; i++) {
zan[i].removeAttribute('aria-label', '*');
var zanName = zan[i].innerText;
var ad = zan[i].querySelector('span');
if (ad == null) {
var span = document.createElement("span");
if (zan[i].classList.contains("is-active") || zan[i].classList.contains("is-liked") || zan[i].classList.contains("Button--primary")) {
span.innerHTML = " 取消赞";
zan[i].appendChild(span);
}
else {
if (zanName !== "赞") {
span.innerHTML = " 赞";
}
zan[i].appendChild(span);
}
}
}
//鼓掌
var gz = d.querySelectorAll("button.PinIndex-reactionButton,button.ReactionButton");
for (var i = 0, l = gz.length; i < l; i++) {
gz[i].removeAttribute("aria-label", "*");
var ad = gz[i].querySelector('span');
if (ad == null) {
var span = document.createElement("span");
if (gz[i].classList.contains("Button--primary") || gz[i].classList.contains("is-active")) {
span.innerHTML = " 已鼓掌";
gz[i].appendChild(span);
}
else {
span.innerHTML = " 鼓掌";
gz[i].appendChild(span);
}
}
}
}
function amo(processFunction) {
;
var mcallback = function (records) {
records.forEach(function (record) {
if (record.type == 'childList' && record.addedNodes.length > 0) {
var newNodes = record.addedNodes;
for (var i = 0, len = newNodes.length; i < len; i++) {
if (newNodes[i].nodeType == 1) {
processFunction(newNodes[i]);
}
}
}
});
};
var mo = new MutationObserver(mcallback);
mo.observe(document.body, { 'childList': true, 'subtree': true });
}
})();
//阅读全文的事件函数
function focusA() {
var x = this.parentNode.parentNode.parentNode.children;
for (var i = 0; i < x.length; i++) {
x[i].querySelector("a").focus();
}
}
function focusB() {
var x = this.parentNode.parentNode.parentNode.parentNode.children;
for (var i = 0; i < x.length; i++) {
x[i].querySelector("a").focus();
}
}
document.addEventListener("click", function (event) {
var t = event.target;
var span = t.lastChild;
if (t.classList.contains("VoteButton--up")) {
if (t.classList.contains("is-active")) {
span.innerHTML = " 已赞同";
}
else {
span.innerHTML = " 未赞同";
}
}
//反对按钮
if (t.classList.contains("VoteButton--down")) {
if (t.classList.contains("is-active")) {
span.innerHTML = "已反对";
}
else {
span.innerHTML = "未反对";
}
}
//赞按钮
if (t.classList.contains("LikeButton") || t.classList.contains("CommentItem-likeBtn") || t.classList.contains("PostIndex-voteButton")) {
if (t.classList.contains("is-active") || t.classList.contains("is-liked") || t.classList.contains("Button--primary")) {
(function () {
setTimeout(function () {
span.innerHTML = " 取消赞";
t.removeAttribute('aria-label', '*');
}, 20);
})();
}
else {
(function () {
setTimeout(function () {
span.innerHTML = " 赞";
t.removeAttribute('aria-label', '*');
}, 20);
})();
}
}
//鼓掌
if (t.classList.contains("PinIndex-reactionButton") || t.classList.contains("ReactionButton")) {
if (t.classList.contains("Button--primary") || t.classList.contains("is-active")) {
span.innerHTML = " 已鼓掌";
(function () {
setTimeout(function () {
t.removeAttribute('aria-label', '*');
}, 300);
})();
}
else {
span.innerHTML = " 鼓掌";
(function () {
setTimeout(function () {
t.removeAttribute('aria-label', '*');
}, 300);
})();
}
}
//选中提示
if (t.classList.contains("TopstoryItem-uninterestTag")) {
if (t.classList.contains("is-active")) {
t.setAttribute("aria-checked", "true");
}
else {
t.setAttribute("aria-checked", "false");
}
}
//查看对话
if (t.classList.contains("CommentItem-talkBtn")) {
t.classList.add("focus-viewDialogue");
}
if (t.classList.contains("CommentTopbar-back")) {
var viewDialogue = document.querySelector(".focus-viewDialogue"); {
viewDialogue.classList.remove("focus-viewDialogue");
viewDialogue.focus();
}
}
}, null);
//导航和操作快捷键函数
function shortcutKey(k) {
shareShortcutKey(k);
var focusElement = document.activeElement; var role = focusElement.getAttribute('role'); if (role == 'textbox' || k.altKey || k.ctrlKey) return false;
var feed = document.querySelectorAll('div[focuss]');
var focussValue = this.getAttribute('focuss'); var number = parseInt(focussValue);
if (k.keyCode == 65) {
if (focussValue !== null) {
k.stopPropagation();
feed[number + 1].focus();
}
}
if (k.keyCode == 90) {
if (focussValue !== null) {
k.stopPropagation();
feed[number - 1].focus();
}
}
if (k.keyCode == 87) {
if (this.querySelector('button.ContentItem-rightButton') == null) { this.querySelector('button.ContentItem-more').click(); this.focus(); }
else {
var x = this.querySelector('button.ContentItem-rightButton');
x.focus(); x.click(); this.focus();
}
}
if (k.keyCode == 86) {
if (this.querySelector('button.VoteButton--up') == null) { this.querySelector('button.LikeButton').click(); this.querySelector('button.LikeButton').focus(); }
else { this.querySelector('button.VoteButton--up').click(); this.querySelector('button.VoteButton--up').focus(); }
}
if (k.keyCode == 68) { this.querySelector('button.VoteButton--down').click(); this.querySelector('button.VoteButton--down').focus(); }
var operation = this.querySelectorAll('button.Button--withLabel');
if (k.keyCode == 67) {
if (this.querySelector('div.Comments-container') == null) { operation[0].click(); }
else { operation[0].click(); operation[0].focus(); }
}
if (k.keyCode == 76) { operation[1].focus(); operation[1].click(); }
if (k.keyCode == 83) { operation[2].focus(); operation[2].click(); }
if (k.keyCode == 84) { operation[3].focus(); operation[3].click(); }
}
//导航快捷键在非内容区的功能
document.body.addEventListener("keydown", function (k) {
if (k.altKey && k.keyCode == 53) {
var title = document.title;
alert(title);
}
var focusElement = document.activeElement; var role = focusElement.getAttribute('role'); var input = focusElement.tagName; if (role == 'textbox' || input == 'INPUT' || input == 'TEXTAREA' || role == 'combobox') return false;
var content = document.querySelectorAll('div[focuss]');
for (var i = 0, l = content.length; i < l; i++) {
if (k.keyCode == 65) { content[0].focus(); }
if (k.keyCode == 90) { content[l - 1].focus(); }
}
var downReason = document.querySelectorAll('div.VoteDownReasonMenu-reason');
for (var i = 0; i < downReason.length; i++) {
if (k.keyCode == 49) { downReason[0].click(); }
if (k.keyCode == 50) { downReason[1].click(); }
if (k.keyCode == 51) { downReason[2].click(); }
if (k.keyCode == 52) { downReason[3].click(); }
}
shareShortcutKey(k);
var t = k.target;
if (t.classList.contains('QuestionEdit-reason')) {
if (k.keyCode == 13 || k.keyCode == 32) {
t.click();
}
}
}, null);
//全局快捷键函数
function shareShortcutKey(k) {
if (k.keyCode == 113) { var x = document.querySelector("div.CommentItem"); { x.setAttribute("tabindex", "-1"); x.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; if (gbName == "收起评论" || gbName == "收起评论") { 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"); { 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 gd = document.querySelector('a.zu-button-more'); { gd.setAttribute('tabindex', '0'); gd.focus(); } }
if (k.ctrlKey && k.keyCode == 81) { document.querySelector('a[href="/watch"]').focus(); }
if (k.altKey && k.keyCode == 88) {
document.querySelector('button.NumberBoard-item').focus();
document.querySelector('button.QuestionHeader-edit').focus();
}
}
var audio = new Audio("http://veg.ink/music/sound.mp3");
audio.volume = 0.15;
audio.play();