accessibility_知乎键盘访问优化

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

目前为 2018-01-14 提交的版本。查看 最新版本

// ==UserScript==
// @name        accessibility_知乎键盘访问优化
// @namespace    https://www.zhihu.com/people/yin-xiao-bo-11
// @version      0.4
// @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="93"]'); {
                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");
        }
        //隐藏项目符号
        var fh = d.querySelectorAll('span.zg-bull');
        for (var i = 0, l = fh.length; i < l; i++) {
            var name = fh[i].innerText;
            if (name == '•') {
                fh[i].innerHTML = "";
            }
        }
        //优化对话框访问
        (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");
                    }
                }
            }, 20);
        })();
        //给移除按钮增加名称
        var yc = d.querySelectorAll("button.Tag-remove");
        for (var i = 0, l = yc.length; i < l; i++) {
            yc[i].setAttribute("aria-label", "移除");
        }
        //删除选择语言
        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);
                }
            }
        }

        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);
                }
            }
        }

        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) {
    k.stopPropagation();
    var focusElement = document.activeElement; var role = focusElement.getAttribute('role'); if (role == 'textbox') return false;
    var feed = document.querySelectorAll('div[focuss]');
    var focussValue = this.getAttribute('focuss'); var number = parseInt(focussValue);
    if (k.keyCode == 65) {
        if (focussValue !== null) {
            feed[number + 1].focus();
        }
    }
    if (k.keyCode == 90) {
        if (focussValue !== null) {
            feed[number - 1].focus();
        }
    }
    if (k.keyCode == 87) {
        if (this.querySelector('button.ContentItem-rightButton') == null) { this.querySelector('button.ContentItem-more').click(); this.focus(); }
        else { this.querySelector('button.ContentItem-rightButton').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(); }
    shareShortcutKey(k);
}
//导航快捷键在非内容区的功能
document.body.addEventListener("keydown", function (k) {
    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);
}, 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.QuestionHeader-edit').focus(); }
}

var audio = new Audio("http://veg.ink/music/sound.mp3");
audio.volume = 0.15;
audio.play();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址