canUseDom
function canUseDom() {
return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
}
isStyleNameSupport 浏览器是否支持该样式名称
document.documentElement.style可以访问到的就是支持的
const isStyleNameSupport = (styleName: string | string[]): boolean => {
if (canUseDom() && window.document.documentElement) {
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
const { documentElement } = window.document;
return styleNameList.some(name => name in documentElement.style);
}
return false;
};
isStyleValueSupport 浏览器是否支持该样式值
const isStyleValueSupport = (styleName: string, value: any) => {
if (!isStyleNameSupport(styleName)) {
return false;
}
const ele = document.createElement('div');
const origin = ele.style[styleName];
ele.style[styleName] = value;
return ele.style[styleName] !== origin;
};
isStyleSupport = isStyleNameSupport & isStyleValueSupport
isStyleNameSupport & isStyleValueSupport都支持就行
getScrollBarSize 计算当前浏览器显示滚动条后占用宽度
// 创建内外两个box
const inner = document.createElement('div');
inner.style.width = '100%';
inner.style.height = '200px';
const outer = document.createElement('div');
const outerStyle = outer.style;
// 给外层设定样式 不能影响到正常内容显示 所以不可见 不能遮挡原有事件区域 所以事件透传
// 绝对定位 overflow=hidden 外部高度大于内部高度 触发滚动条
outerStyle.position = 'absolute';
outerStyle.top = '0';
outerStyle.left = '0';
outerStyle.pointerEvents = 'none';
outerStyle.visibility = 'hidden';
outerStyle.width = '200px';
outerStyle.height = '150px';
outerStyle.overflow = 'hidden';
outer.appendChild(inner);
document.body.appendChild(outer);
// 完全包裹的宽度 offsetWidth 没有滚动条的宽度
const widthContained = inner.offsetWidth;
// 带有滚动条的宽度
outer.style.overflow = 'scroll';
let widthScroll = inner.offsetWidth;
// 如果有滚动条时两者都能相等 说明offsetWidth不能用来确定滚动条宽度
// 用box的clientWidth来作为外宽 clientWidth计算box内宽度 不包括滚动条宽度
if (widthContained === widthScroll) {
widthScroll = outer.clientWidth;
}
// 移除冗余dom
document.body.removeChild(outer);
// 内部非滚动条状态时的offsetWidth - 有滚动条时的宽度 = 滚动条占用宽度
cached = widthContained - widthScroll;
ScrollLocker 锁定当前页面不可滚动 在modal弹窗时候可选背景页是否固定
思路:
- overflow: hidden;
- 上锁的时候新旧样式叠加覆盖旧样式 记录旧样式样式表
- 注意计算当前滚动条宽度
- 保证不会闪屏
- 多个的时候不会重复处理
- 解锁后需要还原 所以在设置locker样式的时候 需要记住旧样式 解锁的时候把整个样式用旧样式覆盖回去
// 新旧样式累加
oldStyle = setStyle(
{
width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
overflow: 'hidden',
overflowX: 'hidden',
overflowY: 'hidden',
},
{
element: container,
},
),
// 恢复
setStyle(cacheStyle.get(container), { element: container });
raf
封装了一个请求requestAnimationFrame请求和取消请求的工具
为什么需要这么些工具
某些情况下是跳脱于react体系的,比方说element.style.height = 90