Comments (15)
对于 table 操作列中触发的模态框(虽然已经习惯称之为 Dialog), 我们应该有两种写法,一种是 @ascoders 所说的: 每一个单元格对应一个模态框:
const TdElement = data.map(item => {
return (
<Td>
<Button>详情</Button>
<Modal show={item.show}>{/* balabala.. */}</Modal>
</Td>
)
})
第二种是: 所有行都对应一个模态框,通过父级中一个状态变量来控制展示的内容:
class Table extends Component {
static state = {
activeItem: null,
};
render() {
const { activeItem } = this.state;
return (
<div>
<AbcModel show={!!activeItem} data={activeItem} />
</div>
);
}
}
第二种方案减少了节点数,但是可能会带来的问题是,每次模态框被展示的时候,触发是会是模态框的 更新 (componentDidUpdate) 而不是 新增。当然结合table 中操作的特点,我们可以这样优化:
{activeItem ? <AbcModel show={true{} data={activeItem} /> : null}
这样每次触发模态框的时候,都会创建新的模态框。
from weekly.
现在有非常多的设计倾向于用动画完成流畅的过渡,让 Modal 变得不再突兀。FB 的算一个,还有 dribble 上诸多例子。https://dribbble.com/shots/3206370-Coverage-Modal-Motion-Study
本文有一些重点强调,第一一定要有退出的方式,设定退出键。第二是 Error 提示不能用弹框,我认为这个强调来源于很难将上下文带到 Modal 中,而且我们一般这类提示都会做一些统一的组件。因此在界面该位置作提示是最好的做法。第三是 ARIA 的支持,做为前端一定要注重无障碍网页。
不要忘记前端的初心一直是保障用户体验。
from weekly.
这周讨论的话题是「模态框的最佳实践」,在我看来,模态框的最佳实践就是:能不用就不用。
原生的 window.alert
和 window.confirm
应该是整个 Web 页面中模态对话框的始祖了吧,然而正是这种强制的、阻塞交互的、遮挡页面原本内容的对话框,给用户的使用心智带来极大的摧残。
仔细回想一下,你在浏览网页的时候有没有遇到过各种流氓弹窗?像早些年间这些广告都是用 window.open
在新窗口打开。但是随着浏览器安全策略的升级和杀毒软件功能的延伸,这类流氓推广逐渐销声匿迹;然而在页面内部的弹窗,更是让人防不胜防。
不仅是国内的网站,在访问许多国外的网站时都会弹出一个请求你 subscribe 他们 maillist 的弹窗。这不最近就有人在吐槽 —— No, I Don’t Want to Subscribe to Your Newsletter。
突然想到一个有意思的,当年 IE 还是主流浏览器的时候,alert 炸弹
可谓搞垮 IE 的不二利器。记得上电脑课的时候,在女同学的电脑上打开 IE,地址栏里输一串 javascript: while(true) alert('haha');
,就能在她点「确定」点到崩溃的时候上演一出英雄救美。当然现在浏览器厂商也都学乖了,alert
弹第二次就让用户可以无限屏蔽。
回到这周的话题,我们没有模态对话框真的不能活吗?
我想起了 Facebook 里这个惊艳的设计,它看起来像模态框,但是完全没有上下文切换的成本,不会让你觉得进入了一个新的页面,需要适应过程:
同理,很多原本需要 alert
或 toast
出来的 message,大家也更倾向于使用不阻塞的、自动消失的提示框来呈现。确实需要展示复杂内容的模态对话框,可以考虑使用抽屉式的设计进入页面,至少这种交互方式能让用户感到些许新鲜感。
总的来说,如果真的需要使用模态对话框,请遵循本文提出的最佳实践;如果不是真的需要,请不要使用模态对话框。
from weekly.
本文从视觉角度分析了模态框,我从代码的角度分析一下。
如果下定决心使用 Modal
,我们必定要考虑支持全部模态框主流的用法,典型代表是 Antd Modal 对关闭按钮、蒙层、异步、页脚、国际化、动画回调以及自定义接口都有成熟的考虑了,这里不赘述,重点在于业务代码对于有状态于无状态模态框的使用方式存在普遍问题。
对有状态模态框来说,很多库会支持 .show
直接调用的方式,那么模态框内部渲染逻辑,会在此方法执行时执行,没有什么问题。不过现在流行无状态模态框(Stateless Modal),模态框的显示与否交由父级组件控制,我们只要将模态框代码预先写好,由外部控制是否显示。
这种无状态模态框的方式,在模态框需要显示复杂逻辑的场景中,会自然将初始化逻辑写在父级,当模态框出现在循环列表中,往往会引发首屏触发 2-30 次模态框初始化运算,而这些运算最佳状态是模态框显示时执行一次,由于模态框同一时间只会出现一个,最次也是首屏初始化一次,但下面看似没问题的代码往往会引发性能危机:
const TdElement = data.map(item => {
return (
<Td>
<Button>详情</Button>
<Modal show={item.show}>{/* balabala.. */}</Modal>
</Td>
)
})
上面代码初始化执行了 N 个模态框初始化代码,在模态框复杂的时候,我们还会封装一层:
class CustomModal {
init() {
// balabala..
}
render() {
return <Modal/>
}
}
引用了这种自定义模态框更是加剧了初始化时计算消耗,为了避免这个问题,尽量将模态框与按钮再封装一个组件,模态框内数据的初始化行为在其状态变更为 show=true
时再执行:
class CustomModal {
init() {
// do nothing
}
componentWillReceiveProps(nextProps) {
if (nextProps.show) {
// balabala..
}
}
render() {
return <Modal/>
}
}
from weekly.
对于问题作者提到的模态框的第四种使用情况:
Show additional information (not in context)
我们也是经常会用到,例如用于展示一些明细数据,对于这种情况,模态框一般不会很小,我看过两种替代方案:
- 新增一个页面展示明细数据
- 通过下拉展示明细数据
对于用户而言,这三种方案哪种更加打扰用户呢?
另外作者的文中如果有数据证明方案的优劣感觉会更加有说服力。
from weekly.
手机打字,没有md的效果,见谅。
很欣赏之前一个comment的说法,将Modal分为stateless和state两种,其实我更习惯地将弹出层分为toast,modal。
首先是toast,它和后者有明确的区别,它一定是stateless的,并且不会引起页面的遮罩,而且,它可以有多个,定时消失什么的都是必须的。toast一般是小小的,主要内容是提示信息,包括非操作(比如网络情况)与操作结果(比如发送成功)这两种。这里有个个人的想法,toast可能更适用于桌面端,毕竟如果在移动端小小屏幕上一下子同时出现好几个toast,还是挺别扭的。
至于后者,也分为移动端和桌面端。移动端上的state的modal不太适合包含过多的操作,否则完全可以做一个单独的页面或者一个弹出层,modal从宽度大小上来说要小于可视区大小。stateless的modal我一般会这么做,在根组件上放一个,用来拦截全局的错误,比如网络情况,而具体页面根据需要放置,但是单个页面级组件只需要一个。至于内容的渲染我选择是基于配置的渲染方式,定义一系列modal内容模板的map,然后传入modal的type和相应的参数,这样将内容和modal解耦。
桌面端的modal作用还是比较大的,可以进行比较复杂的交互操作。我在桌面端很少用modal做提示,除了是权限相关的,需要在提示同时禁止当前用户操作。
另外对一个comment有点疑问,为什么一个table中需要渲染多个modal呢?之前尝试过,且不说多modal渲染带来的性能瓶颈,一个页面中的modal只能同时显示一个,多modal的适用场景是?
一点浅见,希望与大家讨论一下。
from weekly.
@monkingxue 多个 modal 并不是一起显示,而是满足条件后触发显示,很多代码都是这么写的。
from weekly.
@arcthur
可能我们对于 modal 的定义不太一样?我理解中的 modal 是必须要有遮罩层的,这就限制了一次只能显示一个。
如果一次只能显示一个,那为什么不共用一个 modal 组件,只是修改显示的内容呢?原来的项目中出现复杂一点的 state modal 超过20个就会出现渲染卡顿,后来就是整个页面只用一个 state modal 和一个 stateless modal ,一下子就流畅了很多。
from weekly.
模态框的滥用是导致其臭名昭著的根本原因。在用户不知情或不需要的情况下,通过模态框的方式来引起用户的注意。这样不仅打断了用户的交互操作,而且会让用户养成看到模态框就下意识关闭的习惯。
但我依旧坚持,在遵守最佳实践的前提下,模态框是可以给用户带来轻量级体验的。首先,它的出现与消失不需要刷新页面,与页面切换相比,成本要小很多,例如,用户在淘宝上看中了一款商品,想登陆购买,此时弹出登陆模态框的体验就要远远好于跳转到登陆页面,因为用户在模态框中登陆后,就可以直接购买了。其次,模态框的内容对于当前页面来说是一种衍生或补充,可以让用户更为专注去阅读或者填写一些内容。
from weekly.
说点自己的看法。我是比较同意 淡苍 的观点。
模态框的不合理使用导致一大部分用户对它比较嫌弃,其根源是开发者的不合理滥用,却要模态框背锅,这就好比刀被拿去杀人,最后大家都在说刀的错。
其实在合适的场景下使用模态框是能够起到提升体验的作用。
它的些许优点:
(1)由于模态框具备阻塞、吸引注意等特点,是很适合用于提醒、确认等场景,如进行线上同步、删除或变更等操作时,模态框可以很大程度上起到提醒的作用,避免一些误操作;
(2)当然,有些场景只是需要展示内容,如果使用第一种会让用户觉得操作麻烦,体验受阻,那么我们可以将模态框设计成没有任何操作按钮,同时允许用户点击框外的内容来关闭它,这样用户的整体操作就会流畅很多;
(3)模态框相对于页面跳转等操作要轻量的多;
更好的使用它 & 某些不适用的场景:
(1)模态框是作为当前页面的一种衍生或补充,如果其内容与当前内容毫不相干,那么可以使用其他操作(如新页面跳转)来替代模态框;
(2)对于模态框的大小应该要有相对严格的限制,如果内容过多导致模态框或页面出现滚动条,这样的体验很糟糕,这种场景我更愿意在当前页面上或新页面上展示这些内容,而不是全部塞进模态框;
(3)模态框内部应该避免有过多的操作,它应该给用户一种看完即走,而且走的流畅潇洒的感觉,而不是利用繁杂的交互留住或牵制住用户;
(4)避免出现1个以上的模态框,这加深了产品的垂直深度,提高了视觉复杂度,而且会让用户烦躁起来;
(5)不要突然打开或自动打开模态框,这个操作应该是用户主动触发的;
关于模态框,我个人感觉它的视觉交互设计&合理使用远比它的开发要重要的多,所以我这里就不讨论模态框开发的问题了。
from weekly.
@monkingxue 回复你的为什么一个table中需要渲染多个modal呢,这里并不是说多个同时显示,是点击 ID 后显示对应的详情。
多modal的适用场景还有(可能是 bad design)
- 一次只显示一个。但根据触发源不同,内容完全不同。比如点击表格中 ID 列后使用 Modal 展示对应的详情。这种只需要使用 @xile611 第二种方式就可以解决
- 多层级同时显示。比如使用 Modal 来显示一个复杂的表单,在表单保存前再弹 Modal 让用户做最后确认。这需要让 Modal 支持多层级,同时控制蒙层的层级。
- 多个无遮罩同时显示。如显示多个 Modal 对表格中多个来回有关联的数据做处理。这时候一般不需要蒙层,让 Modal 支持可拖动改变位置,同时为 Modal 添加 focus 效果来标记活跃者。
前面 @twobin 的总结已经比较完善,对于面向大众用户的产品,由于用户的关注点和黏性比较低,应该尽可能少的使用 Modal。我补充一个细分的场景。在一些围绕数据来做复杂处理的应用中,如 ERP、CRM,用户通常关注点都在一个表单和围绕表单做的一系列操作,页面来回切换或复杂的看似酷炫的动画可能都会影响效率。用户需要的是直截了当的完成操作,这时候 Modal 就显出了它的优势,但也要注意不能滥用。
from weekly.
@camsong 烂用 -> 滥用
from weekly.
从用户体验上来说,模态框有这些类似组件: Modal
、Notification
、Message
以及 Popover
等等。其各有适用场景,我们不能一味只用 Modal
来替代所有场景。比如轻量级的反馈提醒可以用 Message
。
另外,结合 react
,提一个大家可能都碰到的问题,目前大部分 Modal
实现方式都是:
ReactDOM.render(content, document.createElement('div'));
这种方式的问题是 react
的 context
不能传入到模态框中。目前可以如下解决这个问题:
ReactDOM.unstable_renderSubtreeIntoContainer(content, document.createElement('div'));
from weekly.
在这篇文章中我看到了一个细节,关于模态框的退出方式。在文章里,提到了四种退出方式:
- 取消按钮 Cancel button
- 右上角的关闭按钮 Close button
- ESC键 Escape key
- 点击模态框外的范围 Click outside the window
回想一下那些年自己写的模态框,能做到这四种都俱备的又有多少呢?尤其是后面两个也真是容易忽略的重灾区。但是,走出理想,不可忽视的是现实中,模态框作为一种强干扰的组件,往往使用他的场景,都是不希望用户能太轻松就把它关掉了。这和之前提到的最佳实践就是一个矛盾。
如果只是一般的消息提醒,大可用信息条、小红点,甚至跑马灯这些方式,至少是不阻塞用户操作的。在文末引用的10 Guidelines to Consider when using Overlays一文中,第8条强调了模态框不到万不得以不应该使用。但是真到万不得已的时候,你觉得是使用恶心的交互突出内容吸引用户,还是用更好的体验留住用户?这个真不好回答?对于产品而言,前者如洪水猛兽,后者则是细水长流,是各有利弊的。
作为开发和交互,说服产品的设计规划,尊重用户的使用体验是我们的义务。但是我依旧认为,不应该为了追求极致的用户体验放弃产品本身需要重点突出的内容——也就是说,有些时候,该恶心的地方还是得恶心。(对此观点,虚心接受各方批判。)
随后的一句话,让我对这一情形进一步深思:
Accessibility Tip: each modal window must have a keyboard accessible control to close that window. Ex. escape key should close the window.
Accessibility,用苹果的翻译是“辅助功能”,是对不同终端用户的体验完善。上面这句话就是在说,每一个模态框,都要有通过键盘关闭的功能,通常使用ESC键。似乎我们程序员多少总会把我们自我的惯性思维带进实现的产品,尤其是当我们敲着外置的键盘,用着Mac的时候。下面的这些是我遇到过的:
- 你是否考虑过,用户可能没有鼠标,或者没有键盘,甚至可能既没有鼠标也没有键盘,比如使用的是语音控制?(上例,你让这些用户如何退出)
- 你是否考虑过,很多的Windows PC都已经获得了很好的触屏支持,而你的网页依旧只支持了键盘跟鼠标?(同样的组件是不是也考虑了Touch相关的事件)
- 你是否想过,在没有苹果触摸板的地方,横向滚动条是个逆天的设计?(鼠标拖滚动条,或者Shift+滚轮)
- 你是否想过,在网页里,使用Command(Ctrl) and +/-和使用触摸板的缩放事件是两个不同的表现?(前者是将网页的内容放大,后者更接近放大镜。)
- 接上一问,如果你的终端用户没有好用的触摸板,但是他的确看不清你的网页上的内容。如果他用了前者,你能不能保证你的网页依然能够正常展示内容?(试一试,放大到150%?还能看么?比如亚马逊300%也能正常使用)
这样的点还有很多,平时自己做的那些东西,还真不敢称最佳实践。
from weekly.
有种交互场景使用Modal我还是挺喜欢的。
点开quora首页的feed流,弹出的一个沉浸式的阅读Modal,配合escape键效率不要太高。
而知乎是一点就拉出来很长,不喜欢还要挪动鼠标去点击收起,这里我还是偏向于quora的方案。
from weekly.
Related Issues (20)
- 可视化搭建 - 自动批处理与冻结 HOT 2
- 可视化搭建 - 场景实战
- 加班中,请假一次 HOT 1
- 【自荐开源】AI可视化SolidUI HOT 2
- 精读《自由 + 磁贴混合布局》
- 请假一次 🏳️
- 精读《自由布局吸附线的实现》
- 请假一次 HOT 1
- 精读《算法题 - 通配符匹配》
- 这里我想是对应的 '*' 不匹配任何字符? HOT 1
- 精读《算法题 - 统计可以被 K 整除的下标对数目》
- 精读《算法题 - 最小覆盖子串》
- 请假一次
- 精读《算法题 - 地下城游戏》
- 请假一次 HOT 6
- 精读《VisActor 数据可视化工具》
- 精读《算法题 - 编辑距离》
- 请假一次
- 精读《算法题 - 二叉树中的最大路径和》
- 休刊一段时间 HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from weekly.