ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

rsuite DOMHelper 样式操作实战:addStyle / removeStyle / getStyle 的用法与实现原理

rsuite DOMHelper 样式操作实战:addStyle / removeStyle / getStyle 的用法与实现原理 前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载本篇技术指南以 rsuite 文档站中 style-helper.md 为骨架完整讲解DOMHelper中样式操作三件套——addStyle、removeStyle、getStyle的 API 签名、可运行示例与底层封装原理并结合 src/DOMHelper/index.ts、src/Animation/Collapse.tsx 等源码展示它们在 rsuite 组件内部的实际调用场景。读完本文你将掌握在 React 项目中安全、精准地读写元素内联样式的标准做法并能直接复用到自己的业务组件中。为什么 React 项目还需要直接操作 DOMReact 官方不推荐直接操作 DOM但在 rsuite 组件内部出于动画测量、弹出层定位、滚动容器控制等考虑必须绕过 React 状态直接操作真实 DOM 节点。正如 DOMHelper 文档 所述在 React 项目中我们不推荐直接操作 DOM但是在 RSUITE 组件内部为了一些考虑不得不直接操作 DOM如果您也有类似的需求可以直接使用这组方法。因此 rsuite 将一套 DOM 工具函数以DOMHelper的形式对外暴露。你不需要依赖 jQuery 或其他第三方库只需从rsuite引入即可获得跨浏览器行为一致的 DOM 操作能力。获取与导入 DOMHelperDOMHelper从rsuite包名直接导出可以在组件中解构出需要的子方法import { ButtonToolbar, Button, DOMHelper } from rsuite; const { addStyle, removeStyle, getStyle } DOMHelper;从 src/DOMHelper/index.ts 的源码结构看DOMHelper是对dom-librsuite 项目使用的 DOM 工具库见 package.json 中的dom-lib: ^3.3.1依赖的一次聚合再导出并额外追加了一个自定义的isElement判定方法import * as helpers from dom-lib; import isElement from ./isElement; export * from dom-lib; /** * a wrapper of dom-lib with some custom methods. */ export const DOMHelper { ...helpers, isElement }; export default DOMHelper;也就是说DOMHelper的样式、事件、滚动、查询等一系列方法都来自dom-lib而 rsuite 通过DOMHelper统一命名空间对外提供同时支持具名导入如import { addStyle } from rsuite与整体解构两种方式。style 系列 API 签名总览根据 DOMHelper API 文档style 类别共三个方法均针对HTMLElement的内联样式element.style进行操作同时支持单属性和批量对象两种重载addStyle: (node: HTMLElement, property: string, value: string) void; addStyle: (node: HTMLElement, style: Object) void; removeStyle: (node: HTMLElement, property: string) void; removeStyle: (node: HTMLElement, propertys: Arraystring) void; getStyle: (node: HTMLElement, property: string) string; getStyle: (node: HTMLElement) Object;要点速览方法参数形式返回值作用addStyle(node, property, value)属性名 值void写入单个内联样式addStyle(node, styleObject)样式对象void批量写入多个内联样式removeStyle(node, property)属性名void移除单个内联样式removeStyle(node, propertys[])属性名数组void批量移除多个内联样式getStyle(node, property)属性名string读取指定样式的计算值getStyle(node)无Object读取节点全部计算样式完整演示一个可运行的 addStyle / removeStyle / getStyle 示例官方文档 style-helper.md 提供了一个可直接运行的交互式示例页面中渲染一个div classview目标节点三个按钮分别演示写入、移除、读取内联样式并通过innerHTML实时回显 DOM 变化import { ButtonToolbar, Button, DOMHelper } from rsuite; const { addStyle, removeStyle, getStyle } DOMHelper; const App () { const [html, setHtml] React.useState(div classview/div); const containerRef React.useRef(); const viewRef React.useRef(); const viewHtmlCode () { setHtml(containerRef.current.innerHTML); }; return ( div div {html}/div div ref{containerRef} div classNameview ref{viewRef} / /div hr / ButtonToolbar Button onClick{() { // 批量写入两条内联样式 addStyle(viewRef.current, { font-size: 16px, color: #F00 }); viewHtmlCode(); }} addStyle /Button Button onClick{() { // 按数组批量移除两条内联样式 removeStyle(viewRef.current, [font-size, color]); viewHtmlCode(); }} removeStyle /Button Button onClick{() { // 读取全部计算样式打印到控制台 console.log(getStyle(viewRef.current)); // 读取单条样式的计算值弹出提示框 alert(getStyle(viewRef.current, font-size)); }} getStyle /Button /ButtonToolbar /div ); }; ReactDOM.render(App /, document.getElementById(root));运行逻辑梳理viewRef.current指向目标div classview所有样式操作都以它为node参数点击addStyle后目标节点内联样式变为stylefont-size: 16px; color: rgb(255, 0, 0);innerHTML回显结果点击removeStyle后两条内联样式被移除节点恢复初始状态点击getStyle时console.log(getStyle(node))输出完整的计算样式对象alert(getStyle(node, font-size))弹出font-size的最终计算值。addStyle批量写入内联样式addStyle负责往目标元素上写入内联样式支持两种调用方式// 方式一单属性写入 addStyle(node, font-size, 16px); // 方式二批量对象写入 addStyle(node, { font-size: 16px, color: #F00 });使用注意点属性名既可以使用 kebab-casefont-size也可以使用 camelCasefontSize示例中采用的font-size写法与 DOM 内联样式属性名完全一致最直观值既可以传字符串16px、#F00也可以传数字如addStyle(node, opacity, 0.5)与 React 的styleprop 不同addStyle直接修改element.style不受 React 渲染协调reconciliation的控制适合在事件回调、动画帧、测量等命令式场景中使用。在 rsuite 组件内部addStyle被大量用于动画过程中临时修改尺寸。例如 src/Animation/Collapse.tsx 的折叠动画在进入、退出、过渡完成等不同阶段分别写入0、scrollHeight计算值与autoconst handleEnter useCallback( (elem: HTMLElement) { addStyle(elem, dimension, 0); }, [dimension] ); const handleEntering useCallback( (elem: HTMLElement) { addStyle(elem, dimension, getScrollDimensionValue(elem, dimension)); }, [dimension] ); const handleEntered useCallback( (elem: HTMLElement) { addStyle(elem, dimension, auto); }, [dimension] );这段代码展示了addStyle的典型用法写入数值0配合浏览器对单位属性的自动处理、写入带单位的字符串${value}px以及写入关键字auto覆盖了内联样式操作的大多数取值形态。removeStyle移除内联样式removeStyle与addStyle对称负责删除元素上已写入的内联样式同样支持单个属性与数组批量两种形式// 移除单个属性 removeStyle(node, font-size); // 批量移除多个属性 removeStyle(node, [font-size, color]);当批量移除时数组中的属性会依次从element.style中清除元素将回落到样式表class 或全局样式中定义的默认值。这也是示例中点击 removeStyle 后div classview恢复初始外观的原因。getStyle读取计算样式getStyle用于读取样式值两种重载的区别在于是否传属性名// 读取单条样式的计算值返回 string const fontSize getStyle(node, font-size); // 读取节点的完整计算样式返回 Object const allStyles getStyle(node);值得注意的能力是getStyle同样可以读取 CSS 自定义属性CSS Variables。rsuite 的测试用例 src/Badge/test/Badge.styles.spec.tsx 中就通过getStyle断言了--rs-badge-offset-x、--rs-badge-move等变量值expect(getStyle(badgeElement, --rs-badge-offset-x)).to.equal(5%); expect(getStyle(badgeElement, --rs-badge-offset-y)).to.equal(5%); expect(getStyle(badgeElement, --rs-badge-move)).to.equal(40%);此外src/Animation/Collapse.tsx 的defaultGetDimensionValue还展示了读取marginTop/marginBottom计算值并参与动画尺寸计算的组合用法const value get(elem, offset${capitalize(dimension)}) ?? 0; const margins MARGINS[dimension]; return ( value parseInt(getStyle(elem, margins[0]) as string, 10) parseInt(getStyle(elem, margins[1]) as string, 10) );这里先以offsetHeight/offsetWidth获取内容尺寸再通过getStyle读取上下/左右 margin 并转成数字相加从而精确计算折叠动画的目标尺寸。源码实现DOMHelper 是 dom-lib 的一层薄封装从 src/DOMHelper/index.ts 可以看到addStyle、removeStyle、getStyle均直接来源于dom-lib并通过export *透传rsuite 本身只补充了一个自定义的isElement方法定义于 src/DOMHelper/isElement.tsconst isElement (value: any): value is HTMLElement { return value?.nodeType 1 typeof value?.nodeName string; };该实现通过nodeType 1元素节点与nodeName为字符串两个条件判定目标是否为元素并配合完整的类型守卫与单测覆盖见 src/DOMHelper/test/isElement.spec.tsit(Should be an element node, () { expect(isElement(document.createElement(div))).to.be.true; expect(isElement(document.createElementNS(http://www.w3.org/2000/svg, svg))).to.be.true; }); it(Should not be an element node, () { expect(isElement(undefined)).to.be.false; expect(isElement(null)).to.be.false; expect(isElement({})).to.be.false; ... });测试覆盖了div、svg等元素节点返回true以及undefined、null、普通对象、属性节点、document、文本节点、文档片段返回false的边界情形说明isElement对非元素节点做了充分防御。源码中的真实调用样式辅助函数如何支撑组件内部逻辑除 Collapse 折叠动画 外rsuite 内部还有多处基于这套样式 API 的命令式实现可作为你借鉴的实战范式Slider 拖拽时的 Tooltip 定位src/Slider/useDrag.ts 在拖拽过程中用getWidth测量 tooltip 宽度并通过addStyle写入 CSS 变量来实时调整提示框偏移量const setTooltipPosition useCallback(() { const tooltipElement tooltipRef.current; if (tooltip tooltipElement) { const width getWidth(tooltipElement); // 通过内联 CSS 变量控制 tooltip 偏移 addStyle(tooltipElement, --rs-tooltip-offset, -${width / 2}px); } }, [tooltip]);Badge 主题样式断言src/Badge/test/Badge.styles.spec.tsx 使用getStyle读取背景色与 CSS 变量验证不同尺寸下徽章的渲染结果。CustomProvider 主题切换src/CustomProvider/CustomProvider.tsx 从../DOMHelper具名导入addClass、removeClass、canUseDOM用于在根节点上切换主题 class。这些调用共同说明DOMHelper不是孤立的工具集合而是 rsuite 动画、定位、主题系统等底层能力的公共基石。使用注意事项基于上述示例与源码在使用 style 系列方法时有几点需要留意与 React 状态管理的边界addStyle/removeStyle是命令式 DOM 操作不经过 React 渲染管线。示例中需要借助innerHTML回显来观察变化正是这一点的体现。日常业务中建议只在事件回调、动画生命周期、测量等命令式场景使用避免在 render 阶段直接调用导致与 React 协调结果不一致。值的形式写入时既支持数字也支持字符串读取时getStyle返回的是浏览器计算后的字符串例如颜色会被规范化为rgb(255, 0, 0)形式如示例中alert(getStyle(node, font-size))弹出的即为计算值字符串。需要参与数值运算时参考 Collapse.tsx 的做法用parseInt(..., 10)转换。CSS 自定义属性getStyle与addStyle都能处理--xxx形式的 CSS 变量这对基于设计令牌design tokens做主题定制非常实用。node 参数有效性DOMHelper的方法面向真实HTMLElement传参前可结合isElement做防御性校验避免在节点尚未挂载ref 为 null时调用。延伸阅读DOMHelper 的其余能力一览style 系列只是DOMHelper的一部分官方文档 DOMHelper 索引页 还按类别提供了如下能力每个类别都配有可运行的演示 fragmentclasshasClass/addClass/removeClass/toggleClass示例见 class-helper.mdeventson/off支持返回{ off }解绑句柄示例见 event-helper.mdscrollscrollLeft/scrollTop读值/写值双向示例见 scroll-helper.mdquerygetHeight/getWidth/getOffset/getOffsetParent/getPosition/contains示例见 query.mdDOMMouseMoveTracker鼠标拖拽追踪器提供captureMouseMoves/releaseMouseMoves等能力示例见 dom-mouse-move-tracker.mdSlider 的拖拽实现useDrag.ts即基于类似的PointerMoveTracker思路。当你需要在自己组件中直接操作 DOM 时先检查DOMHelper是否已覆盖该需求通常无需再引入额外依赖——这与 rsuite 自身组件内部不得不操作 DOM 时统一走DOMHelper的设计一脉相承。赞分享前端UI组件【免费下载链接】rsuite A suite of React components .项目地址https://gitcode.com/gh_mirrors/rs/rsuite点击查看免费下载相关推荐rsuite ButtonGroup 分隔按钮divided 属性的用法、实现原理与样式机制rsuite ButtonGroup 分隔按钮divided 属性的用法、实现原理与样式机制 在 rsuite 中 ButtonGroup 的 divid前端UI组件RSuite Divider 标签Label详解label 与 labelPlacement 的用法、样式原理与组件化实战RSuite Divider 标签Label详解label 与 labelPlacement 的用法、样式原理与组件化实战 本文围绕 RSuite 组件库前端UI组件rsuite Calendar 自定义单元格样式深入解析 cellClassName 的用法与实现原理rsuite Calendar 自定义单元格样式深入解析 cellClassName 的用法与实现原理 导读 本文围绕 rsuite 的 Calendar 前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表