一、开发边界情况
Web页面在客户端中展示时,开发者面对的情况会很复杂,比如数据是动态的,设备是多样的等。
1. 还原 UI
还原UI 界面的开发,写代码前先思考以下问题:
- 文本超长应该怎么处理?直接裁切,还是省略?
- 数据为空如何展示?不处理,还是将空白容器隐藏,还是展示占位符?
-
1.1 阻止 input 自动填充
使用 autocomplete=”new-password” 阻止自动填充
<input type="text" id="cc" name="cc" autocomplete="new-password">
1.2 阻止 input 拼写检查
使用 spellcheck = “false”, 设置在可能的情况下关闭对元素内容拼写检查
<p contenteditable spellcheck="false">This exampull.</p>
2. 防御式 CSS
2.1 尽量不显示设置元素宽高
design to code 的过程,尽量不显示设置元素宽高,以保证动态数据不破坏布局
为避免内容超出容器
- 用 min-width 代替 width,min-height 代替 height
实现平滑的折叠、展开:
设置为 flex 容器,它的宽度和父容器等宽,即 width 为 100%,也称块Flexbox容器
- 设置为 inline-flex容器,它的宽度是由其子元素(后代元素)的内容来决定,相当于 width 为 auto,也称内联Flexbox容器
所以应该根据UI的视觉形态来正确的声明Flexbox容器,不能将所有Flexbox容器都声明为块盒!
2.3 考虑Flexbox中的换行
尽量在Flexbox容器上使用 flex-wrap 来避免意外布局的行为。但 flex-wrap: wrap 只有在Flex项目不能自动收缩扩展状态下有效,换句话说,如果在Flex项目中显式设置了 flex: 1时,即使你在Flexbox容器上显式设置flex-wrap为wrap也不能让Flex项目换行
2.4 注意 flex:1 不一定能均分列
flex:1 相当于:
- flex-grow: 1;
- flex-shrink: 1;
- flex-basis: 0%
如果未显式设置flex(它是flex-grow、flex-shrink 和 flex-basis的简写属性)时,其初始值是:
- flex-grow: 0;
- flex-shrink: 1;
- flex-basis: auto
当flex-basis 为 auto 时,Flex项目的宽度是max-content。也就是说,flex:1时,flex-basis变成了0%,这将覆盖Flex项目的内在尺寸(max-content)。flex-shrink不再做任何事情。
“默认情况下,弹性Flex项目(设置为flex:1的Flex项目)在收缩的时候,其宽度不会小于其最小内容尺寸(即 min-width,也就是max-content或固定尺寸元素的长度)。要改变这一点,需要显式设置min-width或min-height的值”
因此,要真正达到均分列,只显式设置 flex:1 还不行,还需要在Flex项目上显式设置min-width的值为0
.item{
flex:1 1 0%;
min-width:0
}
2.5 图片的宽高比与伸缩问题
当用户上传的图片与页面要展示的图片宽高比一样时,图片就会被拉伸,针对img元素展示的图片,可根据 object-fit 属性来控制展示方式:
- object-fit: cover
- object-fit: contain
此外,若图片设置的固定的宽或固定的高,不想要图片展示失去过多重要细节,则可设置图片的宽高比属性:
- aspect-ratio: 4 / 3
2.6 图片上的文字
图片上的文字和图片间通常会有一层蒙层,以增加文本的可读性。如 ```css .card__content { background-image: linear-gradient( to top, hsla(0, 0%, 0%, 0.62) 0%, hsla(0, 0%, 0%, 0.614) 7.5%, hsla(0, 0%, 0%, 0.596) 13.5%, hsla(0, 0%, 0%, 0.569) 18.2%, hsla(0, 0%, 0%, 0.533) 22%, hsla(0, 0%, 0%, 0.49) 25.3%, hsla(0, 0%, 0%, 0.441) 28.3%, hsla(0, 0%, 0%, 0.388) 31.4%, hsla(0, 0%, 0%, 0.333) 35%, hsla(0, 0%, 0%, 0.277) 39.3%, hsla(0, 0%, 0%, 0.221) 44.7%, hsla(0, 0%, 0%, 0.167) 51.6%, hsla(0, 0%, 0%, 0.117) 60.2%, hsla(0, 0%, 0%, 0.071) 70.9%, hsla(0, 0%, 0%, 0.032) 84.1%, hsla(0, 0%, 0%, 0) 100% ); color: #fff; }
防御式的 CSS 应该是在 <img> 中设置一个与文本颜色具有一定对比度的颜色,以避免图片加载失败影响文本
```css
img {
background-color:grey
}
2.7 图片的最大宽度
一般来说,不要忘记为所有图片设置max-width: 100%,使图片具有一定的响应式能力:
img{
max-width:100%;
height:auto;
object-fit:cover
}
2.8 暗黑模式下的图片处理
第一种方式,使用 picture 处理,需准备明暗模式下的两种图片
<picture>
<source srcset="settings-dark.png" media="(prefers-color-scheme: dark)">
<source srcset="settings-light.png" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)">
<img src="settings-light.png" id="screenshot" loading="lazy">
</picture>
第二种方式,使用 filter 处理,降低暗黑模式下的图片亮度
@media (prefers-color-scheme: dark) {
:root {
--image-filter: grayscale(50%);
}
img:not([src*=".svg"]) {
filter: var(--image-filter);
}
}
2.9 滚动链的锁定
滚动链(Scroll Chaining)指的是z轴的多个容器都出现了滚动。 现象:一个弹框(Modal)并向下滚动到底部(垂直方向)时,如果继续向下滚动则会引起弹框下方的内容(通常是 body 元素)会继续滚动
解决办法:在顶部的滚动容器中把 CSS 的 overscroll-behavior属性设置为 contain
.modal{
overscroll-behavior-y: contain;
overflow-y:auto
}
2.10 滚动条的空间占用
当内容变长出现了滚动条的时候,会引起布局发生变化,因为滚动条要占用布局元素的空间。对此解决办法:
设置 scrollbar-gutter。另外可使用 scrollbar-behavior : smooth 让滚动行为更顺滑。
.element{
scrollbar-gutter: stable;
scrollbar-behavior: smooth
}
2.11 滚动捕捉
在用户滚动浏览文档时,将其滚动到特定的位置,使用 scroll-snap-type、scroll-snap-align、scroll-snap-stop
.container {
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
scroll-snap-type: x mandatory;
}
img {
scroll-snap-align: center;
scroll-snap-stop: always;
}
2.12 避开 100vh 的坑
iOS 上Safari使用 100vh 长期存在Bug。如果你将一个容器的高度设置为 100vh 时,会导致这个元素有点太高。因为移动端上的 Safari 在计算 100vh 时忽略了它的部分用户界面
第一种解决方法:
body {
height: 100vh;
}
@supports (-webkit-touch-callout: none) {
body {
height: -webkit-fill-available;
}
}
第二种解决办法:
const vhCheck = () => {
// 模拟 vh
let vh = window.innerHeight * 0.01;
// 设置 css 自定义属性
document.documentElement.style.setProperty('--vh', `${vh}px`);
};
useEffect(() => {
vhCheck();
window.addEventListener('resize', vhCheck);
return () => {
window.removeEventListener('resize', vhCheck);
};
}, []);
.my-element {
height: calc(var(--vh, 1vh) * 100);
}
2.13 z-index 失效问题
文档中的层叠上下文由满足以下任意一个条件的元素形成:
- 文档根元素();
- position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;
- position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素
- flex (flexbox (en-US)) 容器的子元素,且 z-index 值不为 auto;
- grid (grid) 容器的子元素,且 z-index 值不为 auto;
- opacity 属性值小于 1 的元素
- mix-blend-mode 属性值不为 normal 的元素;
- 以下任意属性值不为 none 的元素:
- transform
- filter
- perspective
- clip-path
- mask / mask-image / mask-border
- isolation 属性值为 isolate 的元素;
- -webkit-overflow-scrolling 属性值为 touch 的元素;
- will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;
- contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。
解决方案:
- 父级,任意父级,非 body 级别,设置 overflow:hidden
- 使用3D transform变换,比如 translateZ(0)
2.14 注意Grid 容器的固定尺寸
假设我们有一个包含固定宽度 aside 和 自动宽度 main 内容的网格。 CSS 看起来像这样:
由于空间不足,这将在较小的视口尺寸上中断。 为避免此类问题,请在使用上述 CSS 网格时始终使用媒体查询。wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
@media (min-width: 600px) {
.wrapper {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
}
2.15 伪元素 content 中文乱码
关于 unicode 码在html、css、js中如何展示:
html | &# + 10进制编码 + ; |
---|---|
css | \ + 16进制编码 |
js | \u + 4位16进制编码 |
解决办法:
- 将中文转为unicode 编码。工具如:在线工具网,转换后示例:、—>\u3001
- 再将字母 u 去掉
总结:确保HTML、CSS文件使用 UTF-8 格式,并且HTML文档也使用 UFT-8 的字符编码格式,即HTML文档的meta信息包含
2.16 html 空格实体输入
通常 html 中空格实体使用” ”表示,即 no break space,代表为一个空格实体。下表为Unicode 中的空格字符和“零宽度空格” 。常用的空格实体为” ”、” ”、” ”
Code | Name of the character | Width of the character |
---|---|---|
U+0020 | SPACE | Depends on font, typically 1/4 em, often adjusted |
U+00A0 | NO-BREAK SPACE | As a space, but often not adjusted |
U+2002 | EN SPACE (nut) | 1 en (= 1/2 em) |
U+2003 | EM SPACE (mutton) | 1 em (nominally, the height of the font) |
3. 用户输入正则限制
3.1 用户名正则
仅英文、数字、下划线,4-30位长度,不支持下划线开头:
^(?!_)([A-Za-z0-9_]){4,30}$
3.2 密码正则
至少一位大小写字母、数字、特殊字符,6-20位长度:
^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[#?!@$ %^&*-]).{6,20}$
至少一位大小写字母,只能包含字母、数字、特殊字符,6-20位长度:
^(?=.*?[A-Z])(?=.*?[a-z])[A-Za-z0-9#?!@$ %^&*-].{6,20}$
至少包含一位大小写字母,只能输入ASCII 码
!-~
范围,6-20位长度:^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{6,20}$
3.3 手机号正则
共11位数字,1开头,第二位不能为 2
^1[3456789]\d{9}$
二、问题定位
1. 页面未展示出来?
代码逻辑层面
- 页面是否存在条件展示
- 代码兼容性层面
- 使用的 API 在特定浏览器版本下是否完全支持,部分支持
展示层面
设备与浏览器
- 设备类型(台式机电脑、笔记本电脑、平板电脑、手机、手表等)
- 浏览器类型(Chrome、FireFox、Edge、Safari、Safari iOS、IE、QQ、360)及版本
- 缓存
- 浏览器缓存
- 屏幕
- 屏幕(或视窗)大小
- 屏幕像素密度(比如Retina屏幕和非Retina屏)
- 屏幕技术(比如,OLED、LCD、CTR等)
- 交互
- 用户的操作(比如用户对屏幕进行缩放、调整默认字号等)
- 设备色彩样正(比如夜间模式,iOS的暗黑模式等)
- 网络与性能
- 网络速度
- 性能(比如,设备硬件、服务器负载等)
其中最常见的问题来自于浏览器版本不兼容问题、浏览器缓存问题、屏幕大小开发未适配问题、用户操作问题。