您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
针对知乎的屏幕阅读器可访问性优化
当前为
// ==UserScript== // @name accessibility_知乎键盘访问优化 // @namespace https://www.zhihu.com/people/yin-xiao-bo-11 // @version 0.5 // @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 ly = d.querySelectorAll('button.Select-option'); for (var i = 0; i < ly.length; i++) { ly[i].setAttribute('role', 'button'); ly[i].removeAttribute('tabindex', '*'); } //社交帐号登录 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.querySelectorAll('span[data-reactid="94"],span[data-reactid="93"]'); for (var i = 0; i < dl.length; i++) { var name = dl[i].innerText; if (name == '注册' || name == '登录') { dl[i].setAttribute('tabindex', '0'); dl[i].setAttribute('role', 'button'); dl[i].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 == "DIV") break; if (name[0].classList.contains("PushNotifications-icon")) { var div = document.createElement("div"); div.innerHTML = " 消息"; name[0].appendChild(div); } if (name[1].classList.contains("Messages-icon")) { var div = document.createElement("div"); div.innerHTML = " 私信"; name[1].appendChild(div); } 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 == 70) { 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 == 67) { var pl = document.querySelectorAll('div.CommentItem'); for (var i = 0; i < pl.length; i++) { var t = pl[i].getAttribute('tabindex'); if (t !== null) { pl[i].focus(); } } } 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("https://veg.ink/music/sound.mp3"); audio.volume = 0.15; audio.play();