accessibility_知乎键盘访问优化

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

目前为 2018-03-31 提交的版本。查看 最新版本

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

QingJ © 2025

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