canUseDom

  1. function canUseDom() {
  2. return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
  3. }

isStyleNameSupport 浏览器是否支持该样式名称

document.documentElement.style可以访问到的就是支持的

  1. const isStyleNameSupport = (styleName: string | string[]): boolean => {
  2. if (canUseDom() && window.document.documentElement) {
  3. const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
  4. const { documentElement } = window.document;
  5. return styleNameList.some(name => name in documentElement.style);
  6. }
  7. return false;
  8. };

isStyleValueSupport 浏览器是否支持该样式值

  1. const isStyleValueSupport = (styleName: string, value: any) => {
  2. if (!isStyleNameSupport(styleName)) {
  3. return false;
  4. }
  5. const ele = document.createElement('div');
  6. const origin = ele.style[styleName];
  7. ele.style[styleName] = value;
  8. return ele.style[styleName] !== origin;
  9. };

isStyleSupport = isStyleNameSupport & isStyleValueSupport

isStyleNameSupport & isStyleValueSupport都支持就行

getScrollBarSize 计算当前浏览器显示滚动条后占用宽度

  1. // 创建内外两个box
  2. const inner = document.createElement('div');
  3. inner.style.width = '100%';
  4. inner.style.height = '200px';
  5. const outer = document.createElement('div');
  6. const outerStyle = outer.style;
  7. // 给外层设定样式 不能影响到正常内容显示 所以不可见 不能遮挡原有事件区域 所以事件透传
  8. // 绝对定位 overflow=hidden 外部高度大于内部高度 触发滚动条
  9. outerStyle.position = 'absolute';
  10. outerStyle.top = '0';
  11. outerStyle.left = '0';
  12. outerStyle.pointerEvents = 'none';
  13. outerStyle.visibility = 'hidden';
  14. outerStyle.width = '200px';
  15. outerStyle.height = '150px';
  16. outerStyle.overflow = 'hidden';
  17. outer.appendChild(inner);
  18. document.body.appendChild(outer);
  19. // 完全包裹的宽度 offsetWidth 没有滚动条的宽度
  20. const widthContained = inner.offsetWidth;
  21. // 带有滚动条的宽度
  22. outer.style.overflow = 'scroll';
  23. let widthScroll = inner.offsetWidth;
  24. // 如果有滚动条时两者都能相等 说明offsetWidth不能用来确定滚动条宽度
  25. // 用box的clientWidth来作为外宽 clientWidth计算box内宽度 不包括滚动条宽度
  26. if (widthContained === widthScroll) {
  27. widthScroll = outer.clientWidth;
  28. }
  29. // 移除冗余dom
  30. document.body.removeChild(outer);
  31. // 内部非滚动条状态时的offsetWidth - 有滚动条时的宽度 = 滚动条占用宽度
  32. cached = widthContained - widthScroll;

ScrollLocker 锁定当前页面不可滚动 在modal弹窗时候可选背景页是否固定

思路:

  1. overflow: hidden;
  2. 上锁的时候新旧样式叠加覆盖旧样式 记录旧样式样式表
  3. 注意计算当前滚动条宽度
  4. 保证不会闪屏
  5. 多个的时候不会重复处理
  6. 解锁后需要还原 所以在设置locker样式的时候 需要记住旧样式 解锁的时候把整个样式用旧样式覆盖回去
  1. // 新旧样式累加
  2. oldStyle = setStyle(
  3. {
  4. width: scrollBarSize !== 0 ? `calc(100% - ${scrollBarSize}px)` : undefined,
  5. overflow: 'hidden',
  6. overflowX: 'hidden',
  7. overflowY: 'hidden',
  8. },
  9. {
  10. element: container,
  11. },
  12. ),
  13. // 恢复
  14. setStyle(cacheStyle.get(container), { element: container });

raf

封装了一个请求requestAnimationFrame请求和取消请求的工具

为什么需要这么些工具

某些情况下是跳脱于react体系的,比方说element.style.height = 90

1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。

2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。