accessibility_知乎键盘访问优化

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

目前為 2018-04-21 提交的版本,檢視 最新版本

// ==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();