
本文详解如何在 typography 应用中,通过 ResizeObserver 与事件监听协同工作,使可拖拽元素(.resizable)的宽度变化实时反映为 CSS 自定义属性 --_typecsset-text-measure,并同步更新关联的 <input type="number"> 值,达成拖拽与手动输入双向绑定。
本文详解如何在 typography 应用中,通过 resizeobserver 与事件监听协同工作,使可拖拽元素(`.resizable`)的宽度变化实时反映为 css 自定义属性 `--_typecsset-text-measure`,并同步更新关联的 `` 值,达成拖拽与手动输入双向绑定。
在现代排版工具开发中,用户常需通过两种方式调节文本行宽(measure):一是拖拽可视标尺(如 .resizable 元素),二是直接输入数值。二者必须保持状态一致——拖动时输入框自动更新,输入时视图尺寸同步响应。本方案采用原生 Web API 实现高精度、低耦合的双向绑定。
核心机制:ResizeObserver + input 事件双路驱动
关键在于分离「数据源」与「视图更新」逻辑:
- 拖拽路径:利用 ResizeObserver 监听 .resizable 元素的尺寸变化,将其 contentRect.width 转换为 rem 单位(除以根元素 font-size),再写入 CSS 变量,并同步设置 <input> 的 value。
- 输入路径:为所有 input[type="number"] 绑定 input 事件,读取 data-uom 属性(如 "rem"),拼接后写入对应 CSS 变量(如 --_typecsset-text-measure)。
以下是精简可靠的实现代码:
// 1. 统一处理所有 number 输入框
const numberInputs = document.querySelectorAll('input[type="number"]');
numberInputs.forEach(input => {
input.addEventListener('input', function() {
const uom = this.dataset.uom || '';
// 写入对应 CSS 变量,变量名基于 input.name(如 name="typecsset-text-measure" → --_typecsset-text-measure)
document.documentElement.style.setProperty(
`--_${this.name}`,
this.value + uom
);
});
});
// 2. 监听 .resizable 元素的尺寸变化
const dragElement = document.querySelector('.resizable');
const measureInput = document.getElementById('measure'); // 精确获取目标 input
const observeMeasure = new ResizeObserver(mutations => {
const widthPx = mutations[0].contentRect.width;
const rootFontSize = parseInt(getComputedStyle(document.documentElement).fontSize);
const widthRem = parseFloat((widthPx / rootFontSize).toFixed(1));
// 同步更新 CSS 变量和 input 值
document.documentElement.style.setProperty('--_typecsset-text-measure', `${widthRem}rem`);
if (measureInput) measureInput.value = widthRem;
});
observeMeasure.observe(dragElement);注意事项与最佳实践
- ✅ 单位一致性:ResizeObserver 返回的是像素值,务必转换为 rem(px ÷ root font-size)以匹配 CSS 中 max-inline-size: var(--_typecsset-text-measure) 的计算逻辑;若直接设为 px,将导致响应式失效。
- ✅ 精准定位 input:避免使用 document.querySelector('input')(可能命中非目标元素),应通过 id 或 name 明确选取,例如 document.getElementById('measure') 或 document.querySelector('input[name="typecsset-text-measure"]')。
- ✅ CSS 配合:确保 .resizable 元素具备 resize: inline; overflow: scroll;,且其父容器未限制 width,否则 ResizeObserver 将无法捕获有效变化。
- ⚠️ 性能提示:ResizeObserver 回调中避免高频 DOM 操作;当前逻辑仅更新 CSS 变量与单个 input,已足够轻量。如需支持多 measure 控件,可封装为函数并传入 inputId 和 cssVarName 参数。
- ? 兼容性:ResizeObserver 在现代浏览器(Chrome 64+, Firefox 69+, Safari 13.1+)中已广泛支持;旧环境需引入 polyfill。
最终效果是:拖拽 .resizable 区域时,<input id="measure"> 实时显示当前行宽(单位 rem),且页面中所有依赖 --_typecsset-text-measure 的布局(如 .center 的 max-inline-size)即时响应;反之,手动修改输入值,CSS 变量更新后,视觉尺寸立即重绘——真正实现所见即所得的排版调控体验。

















