整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

为知乎增加快捷键

为知乎增加快捷键

近,知乎在桌面端网页上陆续增加了一些快捷键:

  • 在首页、个人页和搜索结果页等页面浏览时[1]:按 J、K 来选中列表中的条目,按 O 来展开或收起全文,按 V、D 来赞同与反对等。另外,按 Esc 也可以退出大部分弹窗与弹出式菜单。
  • 在播放视频时:按 F 让播放器全屏,按 M 让视频静音,按数字 0-9 可以跳转到对应的视频进度(0%-90%)等。
  • 在回答与文章的编辑器中也有一些快捷键:比如按 ?+K 可以插入链接等。把光标移动到工具栏中的按钮上可以查看介绍。

在非输入状态下按 ?(Shift+/)就可以查看快捷键列表

在开发快捷键的过程中,有一些值得分享的经验。

开发快捷键

快捷键的作用域

实现快捷键不困难,因为监听键盘事件很简单。大部分时候,我们只需要在希望响应事件的最外层元素上注册事件就行,而响应事件的回调(比如关闭模态框)也都可以在这个最外层组件内实现。

知乎首页的组件结构

但是这次的情况要更加复杂。以在首页按 V 赞同答案为例:列表内的时间线条目(FeedItem)包括了回答(AnswerItem)在内各种类型的内容,而在回答里的赞同按钮(VoteButton)才是适合响应快捷键事件的地方。在最外侧的 FeedItem 上容易注册事件却无法响应事件,在最内侧的 VoteButton 中可以响应事件却不能简单地注册事件。

如何在 React 的组件关系中优雅地实现子组件(赞同、收藏按钮等)注册快捷键和回调函数、同时直到父组件(整个回答甚至 Feed 条目)的范围内都可以响应快捷键是比较有趣的。

一种简单的想法是:放弃在子组件中注册快捷键,全部在父组件中注册快捷键,在回调的时候调用子组件的实例方法。这会造成以下这些问题:

  • 几乎放弃了组件的层级抽象:从外向里调用子组件实例方法比较 anti-pattern、难以管理(更别提在 React 中,越过接入 connect 等函数的 HOC 来获取真正的组件实例了)。
  • 快捷键会被重复定义和实现:比如在想法、文章和答案等所有引用赞同按钮的父组件中,都注册和实现同样的按 V 赞同的快捷键。
  • 在知乎的组件结构中,这种实现最多只能在位于层级中部的 AnswerItem 范围内进行,因为 FeedItem 的实现不关心子组件内部的业务。

现在知乎使用的方案是:基于 React context 来为所有子组件共享一个「快捷键实例」,在这个「作用域」下的所有子组件声明快捷键时,都会注册到这个实例上面。

参考下方的伪代码:

function FeedItem() {
  const feedItemElement=useRef(null)
  return (
    <ShortcutContext value={feedItemElement}>
      <div className="FeedItem" ref={feedItemElement}>
        <AnswerItem />
      </div>
    </ShortcutContext>
  )
}

在 FeedItem 中,以自身的 HTML element 来初始化一个新的快捷键实例并设置在 context 中,实际的 keydown 等键盘事件是注册在这个 element 上。所以子组件都可以通过 context 在这个实例上注册新的快捷键。

function VoteButton() {
  const handleVote=()=> console.log('voting')
  // 在 useShortcut 中查找当前 context 中存在的快捷键实例,并注册在实例上
  useShortcut('v', handleVote)
  return <button onClick={handleVote}>赞同</button>
}

在 VoteButton 中,就可以像响应点击事件一样注册并响应快捷键事件,看起来和写起来都非常清爽。

列表导航的快捷键

使用 J、K 进行 Vim 风格的列表导航是许多网站常见的快捷键设计[2]。下面是一些如果从零开始实现时,可能会遇到的细节问题:

  • 直接使用 element.focus() 方法会由浏览器决定一个合适的滚动位置,这通常是不符合预期的(对于知乎来说,是因为顶部有一个固定的导航栏,原生滚动经常会被这个导航栏挡住)。我们使用 preventScroll: true 参数禁用了这个原生的滚动,并使用自己计算的结果来 window.scrollTo() 到指定位置。
  • 不是所有元素都是可以被 focus 的。如果只是普通的 <div> 元素的话,可以设置 tabIndex 为 0 或 -1 使其可被 focus。如果设置为 -1 的话则只可以被 focus,但是不能被 Tab 键选中[3]。是否希望被 Tab 选中,主要在于该元素内是否有可以被阅读的元素,或者该元素本身有可交互的行为。
  • 如果在 focus 到某个列表元素后,又用滚轮或者触摸板移动了网页到很远的距离,再按 J、K 进行导航时就需要放弃已经 focus 的元素的,尽可能地 focus 在视图区域内可见的元素上[4],或者采用别的策略[5]
  • 和其他类似产品不同,知乎大部分列表元素不是整个区域都可以点击并进入详情页,而是鼓励在当前页面展开并消费内容的(否则可以直接使用一个 <a> 标签包裹。这次特意实现了 focus 在列表项时,按 Enter 键可以进入详情页的快捷键。
  • 如果因为点击而 focus 在某个列表项时(比如第 2 项),按 J、K 导航时最好只是先在该元素上显示 focus outline,而不是直接进行快捷键导航(到第 1 项或者第 3 项)。因为此时用户不一定知道自己已经 focus 在这个列表项上。这和知乎不为点击行为导致的 focus 添加 outline 有关。

Focus Outline 该在什么时候展现

当使用快捷键进行浏览时,「知道光标在哪里」很重要。屏幕阅读器可以阅读当前 focus 元素的内容。如果不通过声音的话、就只能通过视觉样式来知道当前正在 focus 的元素是什么。

Chrome 为 Input 标签默认添加的 focus outline

浏览器默认会为可 focus 的元素通过 :focus 伪类增加一个 outline 样式,因为这个样式不是很好看,再加上可 focus 的元素往往也会单独设计一些响应点击的样式,所以一般产品或设计会要求工程师取消掉该样式。但是如果简单取消所有 focus 样式后,用鼠标当然知道「我在点哪里」,但在使用键盘访问的场合就根本不知道「我在哪里」了。

最优雅的方案是使用 CSSWG 的 :focus-visible 伪类来添加 focus 样式(同时禁用原先的 :focus 样式),在 WICG 对该样式的 polyfill 中有详细的介绍。简单来说,就是「只有用键盘触发的 focus 才应该添加 focus 样式」。

知乎按钮的 focus 样式

知乎通过和该 polyfill 类似的思想实现了这一设计:在使用键盘操作后,会为 <html> 元素添加 data-focus-visible 属性。只有在包含该属性的情况下,各个元素才会添加 focus 样式。而且知乎还修改了默认的样式、更为美观。你可以使用 Tab 键 tab 到赞同按钮上查看这个效果。

在开发这一部分功能的时候,还有一个特殊的设计:使用 ?+C 等快捷键进行复制粘贴操作(准确地说,是按键中包括 Control 或 Shift 等 Modifier Key)时,不认为是一般的键盘操作,也不展现 focus outline,否则 outline 会过于频繁地时有时无。有意思的是,Twitter 其实也已经做了类似的处理,让人感到大洋两岸的工程师都在为用户体验而努力…

快捷键和可访问性的关系

很多关于快捷键的讨论都会有视障用户的参与,因为使用屏幕阅读器浏览网页、必须使用包括 Tab 键在内的各种键盘快捷键进行光标定位与操作,他们是「最会用键盘刷知乎」的人。但是,对视障用户的支持远不止添加快捷键这么简单。


@devil缠

在一个答案的评论区[6]提到:

如果只是单个按键,(快捷键)基本上没有任何用处。以 V 为例:当用 Tab 到达「赞同」按钮时,直接(按)空格就可以点赞同。(另外)如果在查看内容区点击 V,焦点不会跑到「赞同」按钮上。

合理的快捷键有可能用处不大,而不合理的快捷键不但不能帮助视障用户,还会帮倒忙。

@殷晓波

@devil缠

都提到「使用 V 赞同之后,希望可以 focus 到赞同按钮」,如果不真正使用屏幕阅读器浏览网页,是无法想象这句话的原因的:

快捷键赞同后,需要转移焦点

简单来说,屏幕阅读器只有在焦点改变时才会阅读焦点内的文字,它监控不到「赞同按钮变深蓝」这样普通人可以轻松理解的设计反馈。如果使用快捷键赞同后不改变焦点,连按下键盘发生了什么都不知道,也就不会知道「已经赞同了答案」。此时 tab 到赞同按钮上阅读到「已赞同」的文字才知道发生了什么就很奇怪。

浏览器的点击行为会自动 focus 在可交互的元素上(例如 <button><a>),而此时按 Enter 或 Space 等快捷键可以「模拟一次点击」,这套现成的体系很容易被忽略。在实际体验中,tab 到「阅读全文」按钮再按 Space 来展开全文并不比用快捷键 O 来展开全文麻烦很多。

除此之外,很多读屏软件或者视障用户也会定义、开发个性化的快捷键[6]。这么来看,使用 <button> 等语义化标签使元素可交互元素也可以被 focus、尽可能使用 <a> 而不是在监听 onClick 事件时使用 location.href 进行页面跳转、配置好 aria 属性等…对 Accessibility 更有意义。

总的来说,实现快捷键和实现其他功能一样也要注意视障用户的使用、交互体验,比如:

  • 任何快捷键操作后都要像点击一样转移焦点。
  • 转移焦点后,还需要配置 aria-label 等属性来阅读出有意义的提示文字,比如赞同还是已赞同、反对还是已反对。

快捷键只是 Accessibility 的一部分,而可访问性又是一个更加系统和复杂的工程。知乎做了一些努力[7],但还远远不够。也欢迎对这个领域有更多了解的朋友提出建议。

Q & A

可以关闭快捷键吗?

是的。如果你使用 Vimperator 或者 Vimium 等浏览器扩展定制了快捷键而不想和知乎的冲突,可以在桌面端网页的个人偏好设置(https://www.zhihu.com/settings/preference)中关闭快捷键[8]

这篇文章的注释与参考是如何做到的?

这是编辑器的新功能,会在开放后再行介绍。

参考

^这些快捷键在迁移到新版 Web 页面时没有同步迁移,在很长一段时间内都没有实现。

^包括 Twitter、Facebook、Gmail 与新浪微博等,知乎从这些网站的实现细节中受益良多。

^一个叫 tabbable 的库中有关于这两者区别的介绍,这个库在实现 focus trap 等效果时很有用。 https://github.com/davidtheclark/tabbable

^如何高效地查找离视窗最近、滚动距离最小的元素,这个算法比较有意思,这里不赘述了。

^知乎和 Facebook 与新浪微博一样,会选中视野内可见的新元素,而 Twitter 会放弃滚动。

^ab根据 @devil缠 在这个答案评论区中的说法,他使用的读屏环境还会定义包括 K 下跳 10 个链接、Shift+K 上跳 10 个链接等快捷键 https://www.zhihu.com/question/19842222/answer/17152043

^@长天之云 的答案介绍了一些知乎对 a11y 的支持 https://www.zhihu.com/question/20487917/answer/15265930

^只在当前使用的浏览器中生效。

作者:孙北吉

出处:https://zhuanlan.zhihu.com/p/59928288

arkdown编辑器

Markdown 其实在 2004 年就有了,不过之前一直很小众,这几年随着相关应用平台的发展,Markdown以其独到的优势迅速火起来了。Markdown编辑器使用一套格式标记语言来对文档内容进行排版和格式显示。而Markdown的标记很少,常用的也就十个左右,它是一种非常轻量的标记语言。

Markdown编辑器深受技术人员的喜爱,Markdown可以用来编写说明文档,用它写的文档很多技术平台都能通用;Markdown可以用来写技术博客,可以使说明部分和代码都非常清晰易读;Markdown格式转换方便,还可以轻松地将文本转换为 html、pdf等。

Markdown编辑器分类

按照Markdown编辑器的使用环境,可以将它们归纳为三类。

  1. 平台集成工具:各大在线博客、社区平台自带的写作工具,比如CSDN、博客园、简书等。
  2. 独立软件类:下载到自己机器上使用的独立产品,可以编辑本地文件,比如Mou、MarkdownEditor、Haroopad等。
  3. 插件类:他自己本身是不能独立使用的,可以在你现有的主流编辑器安装,使你现有的编辑器具有Markdown的功能,比如Atom、WebStorm、Sublime Text等。

这三类软件分别面向三类Markdown用户,大家可以根据自己的需求来选择使用。

下面是我收集的一些业界比较受欢迎的Markdown编辑器,喜欢用Markdown的朋友可以一起看看,一起探讨。

1 Mou

Mou 是一款由国人开发的Markdown 编辑器,支持实时预览,但是仅支持 苹果操作系统,可以说是目前最好用的免费 Markdown 编辑器,对汉字兼容性非常好。提供语法高亮、在线预览、同步滚动、全屏模式,支持自定保存、自动匹配,允许自定义主题等等。支持 CSS,HTML 和 PDF 导出等功能。

Mou是独立的软件。

更多介绍及下载:http://25.io/mou/

2 MarkdownPad

MarkdownPad被很多人称赞为windows下最好用的Markdown编辑器之一,不过仅支持windows。它有免费版和收费版(MarkdownPad Pro),一般情况下免费版就够用了,想用pro版的可以自行网上下载,强大的国人crack无处不在的~ ~。

MarkdownPad支持键盘快捷键和工具栏操作,即可添加标记也可移除,支持即时HTML预览、支持自定义配色方案、字体、大小和布局、支持音乐视频,可以导出HTML和PDF。

MarkdownPad是独立的软件。

更多介绍及下载:http://markdownpad.com/

3 Typora

Typora也是非常用名,非常好用的markdown编辑器,它的设计理念很不一样,是真正的即时预览型编辑器,不同于左右两个窗口的编辑器。Typora的设计理念就是极致简洁,它将「写字」和「预览」这两件事情合并了。

如果要修改已经写好的markdown标记可以点击切换到“源代码模式”。

Typora 同样支持 Windows、OS X 和 Linux多个操作系统,支持数学编辑,可与 Word 直接格式转换,可以进行多种文档格式转换。Typora 流畅度和反应速度很快,特别适合那些手速快的人。

Typora是独立的软件。

更多介绍及下载:https://www.typora.io/

4 Atom

Atom 可以说是专门为程序员推出的一个文本编辑器,界面简洁,支持实时预览。功能非常多,除了Markdown同时支持CSS,HTML,JavaScript等网页编程语言,还支持宏定义,自动分屏功能等。Atom还具有语义输入模式,比例输入code即会自动开启代码模式。

Atom支持windows、苹果、linux等多种操作系统。Atom是由著名的github平台出品的。

Atom是独立的软件,也支持插件方式。

更多介绍及下载:https://atom.io/

5 Haroopad

Haroopad也是一款非常流行的编辑器,来自韩国。Haroopad支持 Windows、Mac OS X 和 Linux三大操作系统。Haroopad的特色是主题样式丰富,语法高亮支持无数种编程语言,几乎你能想到的编程语言他都支持。Ubuntu/Linux 用户使用该工具比例很高,Haroopad 也是开源免费的。Haroopad也支持导出HTML、PDF,也支持数学公式和流程图。

Haroopad是独立的软件。

更多介绍及下载:http://pad.haroopress.com/user.html

6 SublimeText

SublimeText界面简约大方,定位专业,功能强大,并具有良好的扩展功能。SublimeText是收费软件,支持windows、苹果、linux三大操作系统。支持的编程语言有十几种,并可通过第三方插件无限扩充。

SublimeText默认不能实时预览,但通过 Markdown Preview 的插件来实现该功能。SublimeText屏幕右边有一个文档缩略图,可以看到文档全貌。

SublimeText是独立的软件,也支持插件方式。

更多介绍及下载:http://www.sublimetext.com/

7 Cmd Markdown

Cmd Markdown是一款不错的写作工具,同时也兼顾博客等写作平台,国内作业部落出品,同时支持Windows、苹果、Linux操作系统,也有 Web 在线创作平台,界面很舒服。

Cmd Markdown是独立的软件、也有平台集成版本。

更多介绍及下载:https://www.zybuluo.com/mdeditor

8 Byword

Byword。一款轻量级的 Markdown 编辑器,比较容易上手,适合新手使用。支持苹果系列系统,包括台式机、iPad和iPhone,收费软件。

Byword是独立的软件。

更多介绍及下载:https://bywordapp.com/

9 CuteMarkEd

CuteMarkEd 是一个基于qt5的Markdown 编辑器,开源免费,支持windows、苹果、linux多种系统平台,提供实时 HTML 预览、数学表达式、代码高亮和PDF导出。

CuteMarkEd是独立的软件。

更多介绍及下载:https://cloose.github.io/CuteMarkEd/

10 Dillinger

Dillinger是国外的 Markdown 编辑器,Web在线创作。漂亮强大,支持md、 html、pdf 文件导出,支持Dropbox、Bitbucket、Github、Google Drive、Onedrive 一键保存,也可以编写本地文件。支持实时预览,跨平台,浏览器打开。

Dillinger是平台集成工具,不是独立的软件。

更多介绍及下载:https://dillinger.io/

11、EpicEditor

EpicEditor 是个嵌入式 JavaScript Markdown 编辑器,可以全屏编辑,在线预览,自动草稿保存,支持离线等功能。对于开发者有很大吸引力,很容易集成在自己的系统中,并很容易自定义,也支持主题自定义。

EpicEditor属于插件类的API工具。

更多介绍及下载:http://ww1.epiceditor.com/

12 MarkdownEditor

MarkdownEditor是一款基于浏览器的 Markdown 编辑器,虽然是独立软件,但该软件内嵌一个浏览器。功能非常简单实用、反应速度很快,号称是Markdown领域的NotePad(记事本)。MarkdownEditor还有拼写检查功能。

MarkdownEditor有两个版本,都是国人开发的,都是开源免费的。

MarkdownEditor是独立的软件、也有在线Web集成版。

更多介绍及下载:http://jbt.github.io/markdown-editor/

13 MarkPad

MarkPad是免费开源的 Markdown 编辑器,界面风格与window系统类似,同时只支持Window。支持实时预览,开放直接保存到你的博客或github的接口,有语法检查,支持代码高亮。

MarkPad是独立的软件。

更多介绍及下载:http://code52.org/DownmarkerWPF/

14 Marxico

Marxico中文名马克飞象,有桌面版、Chrome App插件,也支持Web集成版。这款编辑器的特点是可以直接把文本存到印象笔记平台中,另外还有导出HTML时可以将其中的图片自动转成base64保存。其他的实时预览、语法高亮也都支持。

不过这款软件是收费的~ ~

Marxico是独立的软件、同时有Chrome App插件,也支持Web集成。

更多介绍及下载:http://marxi.co/

实在太困了,就先介绍这么多吧~ ~ ~ (PS:刚接触写博客,发现还真是个累活呀:-P)

最后

markdown编辑器产品非常多,以上只是本人接触过的一些,相信还有很多很好的markdown存在,其实也没必要全都试一遍,只有最适合自己的,才是最好的。

如果你用过其中的一种,或者你用过其他好用的markdown编辑器,欢迎交流分享。

觉这个特效对大神们来说简直so so so so so easy!但我就是要要要要要要要要要发出来,分享给正在学习前端开发的朋友们,代码在下文直接复制走,而且不用说谢谢~嘿嘿。时不时发一点好玩的特效,喜欢的朋友点一下上面的关注哦。我只是小小的前端开发工程师而已,各位大佬别喷我,谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢啦。(你还别说,这支付宝体真挺好玩。)

别喷我,比起让你加群拿代码的那些教育机构的推广者,我连个广告都没打!

先上一个效果图(键盘控制的,效果有点鬼畜!)

按下键盘上的A 网页上的A就会动哦~

代码如下()

<!doctype html>

<html>

<head>

<meta charset="utf-8">

<title>网页键盘钢琴按键特效</title>

<style>

*{

margin:0;

padding:0;

list-style:none;

}

#box{

width:400px;

margin:300px auto 0;

}

ul{

margin-right:-20px;

}

li{

width:48px;

height:48px;

border:1px solid #666;

float:left;

margin-right:20px;

text-align:center;

line-height:48px;

background:#000;

color:#fff;

font-weight:bold;

position:relative;

}

li span{

display:block;

opacity:0.5;

filter:alpha(opacity:50);

}

li p{

width:100%;

height:0;

background:#03F;

position:absolute;

bottom:49px;

left:0;

}

.keyA{

background:#e51c55;

}

.keyS{

background:#c926a5;

}

.keyD{

background:#9c4bd5;

}

.keyJ{

background:#4f65c3;

}

.keyK{

background:#6ccab7;

}

.keyL{

background:#92d55d;

}

</style>

</head>

<body>

<div id="box">

<ul>

<li>

<span class="keyA">A</span>

<p class="keyA"></p>

</li>

<li>

<span class="keyS">S</span>

<p class="keyS"></p>

</li>

<li>

<span class="keyD">D</span>

<p class="keyD"></p>

</li>

<li>

<span class="keyJ">J</span>

<p class="keyJ"></p>

</li>

<li>

<span class="keyK">K</span>

<p class="keyK"></p>

</li>

<li>

<span class="keyL">L</span>

<p class="keyL"></p>

</li>

</ul>

</div>

<script>

window.onload=function(){

var aP=document.getElementsByTagName("p");

var aSpan=document.getElementsByTagName("span");

var aKeyCode=[65,83,68,74,75,76]; //定义键盘按键的键值 65表示大写A 具体键值请参照文章里的ascii表里的值

document.onkeydown=function(event){

var event=event || window.event;

for(var i=0; i<aKeyCode.length; i++){

if(event.keyCode==aKeyCode[i]){

startMove(aP[i],{height:240});

startMove(aSpan[i],{opacity:100})

}

}

}

document.onkeyup=function(event){

var event=event || window.event;

for(var i=0; i<aKeyCode.length; i++){

if(event.keyCode==aKeyCode[i]){

startMove(aP[i],{height:0});

startMove(aSpan[i],{opacity:50})

}

}

}

}

function startMove(obj, json, fnEnd){

clearInterval(obj.timer);

obj.timer=setInterval(function (){

var bSwitch=true;

for(var sAttribute in json){

var iTarget=json[sAttribute];

if(sAttribute=='opacity'){

var cur=Math.round(parseFloat(getStyle(obj, sAttribute))*100);

}else{

var cur=parseInt(getStyle(obj, sAttribute));

}

var speed=(iTarget-cur)/5;

speed=speed>0?Math.ceil(speed):Math.floor(speed);

if(sAttribute=='opacity'){

obj.style.filter='alpha(opacity:'+(cur+speed)+')'; //IE

obj.style.opacity=(cur+speed)/100; //ff chrome

}else{

obj.style[sAttribute]=cur+speed+'px';

}

if(cur!=iTarget){

bSwitch=false;

}

}

if(bSwitch){

clearInterval(obj.timer);

if(fnEnd){

fnEnd();

}

}

}, 30);

}

//获取飞行间样式

function getStyle(obj,sAuttribute){

var result=""

if(window.getComputedStyle){

result=window.getComputedStyle(obj,false)[sAuttribute];

}else{

resule=obj.currentStyle[sAuttribute];

}

return result;

}

</script>

</body>

</html>

ASCII码表

如果哪句代码不理解,可以直接在下面留言,我看到了会一一解释哦。当然啦,你喷我我也会回你。

喜欢JS特效的朋友,记得关注我哦,不定期分享好玩好看的JS特效,前端路上,一起成长~