☰
Tether 示例合集精读:9 个官方 Demo 的配置写法与底层源码对照
2026/9/25 5:32:56 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】tether

A positioning engine to make overlays, tooltips and dropdowns better

项目地址:https://gitcode.com/gh_mirrors/te/tether
点击查看免费下载

Tether 官方文档的 Examples 章节以“示例目录”的形式,给出了从入门到进阶共 9 个可直接运行的定位场景(examples/目录)。本文逐个拆解这些示例的完整配置——包括attachment、targetAttachment、constraints、targetModifier等参数的真实取值——并对照 src/js/tether.js 与 src/js/constraint.js 的源码,解释每种定位策略(越界隐藏、贴边钉住、可见区对齐、滚动条跟随、链式元素)是如何实现的。读完后你可以按图索骥地复现每个 Demo,并理解其背后的调用链。

示例总览:目录结构与运行前提

官方文档 docs/2-Examples/1-list_of_examples.md 将示例分为 Beginner 与 Advanced 两级,页面版导航 examples/index.html 与其内容一一对应:

级别示例解决的问题
Beginnerexamples/simple最基础的双元素定位入门
Beginnerexamples/out-of-bounds元素将要超出屏幕时将其隐藏
Beginnerexamples/pin将元素钉在可视区内,永不越出
Beginnerexamples/enable-disable用 JavaScript 动态启用/禁用 Tether
Advancedexamples/content-visible用visible修饰器对齐到另一元素的可见部分
Advancedexamples/dolls数十个元素链式 tether 的性能演示
Advancedexamples/element-scroll用scroll-handle修饰器跟随元素滚动条
Advancedexamples/scroll用scroll-handle修饰器跟随页面滚动条
Advancedexamples/viewport将元素对齐到视口中央

所有示例共享两个前提:

  1. 需要先构建产物:每个示例的index.html都引用../../dist/js/tether.js,该文件由 rollup.config.js 打包生成。根据 package.json 的 scripts,pnpm build(内部为pnpm clean && rollup -c)会产出dist/,pnpm start则以 watch 模式持续构建;
  2. 需要静态服务器:示例页面是纯静态 HTML。仓库的 Cypress 流程(test:cy:ci)会用http-server -p 9002启动start-test-server,本地运行示例时同样需要任意静态服务器(示例本身不依赖构建工具运行)。

Beginner 示例

simple:最小可用的双元素定位

simple/index.html 是官方推荐的起点,页面包含两个div(.element与.target)和一段 10 行左右的脚本:

<div class="element"></div> <div class="target"></div> <script src="../../dist/js/tether.js"></script> <script> new Tether({ element: '.element', target: '.target', attachment: 'top left', targetAttachment: 'top right', constraints: [{ to: 'window', attachment: 'together' }] }); </script>

配置要点:

  • element/target接受 CSS 选择符字符串。在 src/js/tether.js 的setOptions中,字符串会被document.querySelector解析为真实元素,jQuery 对象则取[0]解包;
  • attachment: 'top left'表示锚定元素自身的左上角,targetAttachment: 'top right'表示锚到目标的右上角,即气泡贴在目标右侧;
  • constraints: [{ to: 'window', attachment: 'together' }]约束两个元素必须保持在窗口内;页面提示“Resize the page to see the Tether flip”——缩小窗口时 Tether 会自动翻转(flip)锚点方向,这正是targetAttachment默认值auto auto参与的结果(默认值见 src/js/tether.js 中的defaults:offset: '0 0'、targetOffset: '0 0'、targetAttachment: 'auto auto'、classPrefix: 'tether')。

该示例由 test/cypress/integration/simple.cy.js 的端到端测试持续验证。

out-of-bounds:越界即隐藏

out-of-bounds/index.html 与 simple 几乎相同,多了两段关键代码:

.tether-element.tether-out-of-bounds { display: none; }
tether.on('update', function(event) { console.log(event); });

原理在 src/js/constraint.js 中:约束模块计算元素是否越界(_calculateOOBAndPinnedLeft/_calculateOOBAndPinnedTop,见 constraint.js),越界时给元素添加tether-out-of-bounds类(由classPrefix(默认tether)与out-of-bounds组合,见 constraint.js);示例中的 CSS 利用这个类把元素直接display: none。配合tether.on('update', ...)事件回调(Tether 每次重定位都会触发),你可以在应用层做更精细的处理,例如淡出或重新锚定。

pin:把元素钉在可视区内

pin/index.html 只改了约束的一项配置:

constraints: [{ to: 'window', pin: true }]

pin的取值在 constraint.js 中解析:传入字符串(如'left')会按逗号拆分为数组;传入true则等价于['top', 'left', 'right', 'bottom'],即四个方向全部钉住。被钉住的每条边会额外生成pinned-top/pinned-left等类名(constraint.js),供 CSS 做进一步样式区分;同时元素位置被钳制在约束边界内,表现为“缩放窗口时元素贴着屏幕边缘移动而永不消失”。

enable-disable:运行时开关

enable-disable/index.html 演示了 Tether 实例的两个生命周期方法:

var tether = new Tether({ element: '.element', target: '.target', attachment: 'top left', targetAttachment: 'top right' }); document.querySelector('.target').addEventListener('click', function(){ if (tether.enabled) tether.disable(); else tether.enable(); });

点击绿色 target 即在启用/禁用之间切换。从源码看(src/js/tether.js),enable()会给元素与目标添加tether-enabled类(同样由classPrefix组合而来)并调用position()执行定位;disable()则移除该类,实例的enabled标志随之翻转,供业务代码做状态判断。这个“CSS 类 + 实例标志”的双重状态设计,使 enable/disable 既能驱动样式又能驱动逻辑。

Advanced 示例

content-visible:对齐到另一元素的“可见部分”

content-visible/index.html 构造了一个 600vh 高的蓝色长条(.content-box),其中有一个 200px 高的黄色.element,核心配置是:

new Tether({ element: '.element', target: '.content-box', attachment: 'middle center', targetAttachment: 'middle center', targetModifier: 'visible' });

targetModifier: 'visible'是整个示例的关键。在 src/js/tether.js 的getTargetBounds中,当targetModifier === 'visible'时,目标边界不再取元素的完整 bounds,而是取getVisibleBounds(bodyElement, target)——即目标元素与视口重叠的“可见部分”的边界。于是.element会始终对齐到.content-box当前露在屏幕内那一段的中央,滚动页面时对话框跟着“可见中段”移动。实现细节(可见区裁剪算法)位于 src/js/utils/bounds.js。

viewport:把元素固定在视口中央

viewport/index.html 是visible修饰器的第二种用法——目标直接取document.body:

new Tether({ element: '.element', target: document.body, attachment: 'middle center', targetAttachment: 'middle center', targetModifier: 'visible' });

body的可见部分恰好就是当前视口,因此.element永远停留在视口正中,页面滚动(示例动态生成了 300 个彩色块制造可滚动内容)也不影响它。页面提示可检查元素最终计算样式,Tether 在此场景下会决定使用position: fixed来维持位置。源码中还有一个等价捷径:src/js/tether.js 里,直接把target写成字符串'viewport'时,会自动改写为document.body+targetModifier: 'visible',与示例的显式写法完全等效。

scroll:跟随页面滚动条

scroll/index.html 在一整页约瑟夫·康拉德的小说正文上,把一个提示条 tether 到页面滚动进度:

new Tether({ element: '.pointer', attachment: 'middle right', targetAttachment: 'middle left', targetModifier: 'scroll-handle', target: document.body });

当targetModifier === 'scroll-handle'时,getTargetBounds返回getScrollHandleBounds(src/js/tether.js),即目标滚动条“拇指”(thumb)的几何位置。于是.pointer在垂直方向上始终与页面滚动拇指同步,随滚动上下移动;示例再叠加一段自定义脚本,把当前章节标题与滚动百分比(pageYOffset / (scrollHeight - innerHeight))写进提示条。注意 src/js/tether.js 中的配套逻辑:scroll-handle修饰器会把scrollParents直接锁定为[this.target],而普通场景则用getScrollParents(target)收集滚动父级,两者决定了滚动监听的注册对象。

element-scroll:跟随“元素内部”的滚动条

element-scroll/index.html 把同样的思路搬到容器级滚动上:页面中有一个position: fixed、overflow-y: scroll的 80vh 阅读框.scroll(并刻意用::-webkit-scrollbar { display: none }隐藏原生滚动条),配置为:

new Tether({ element: pointer, // .pointer target: scroll, // .scroll attachment: 'middle right', targetAttachment: 'middle left', targetModifier: 'scroll-handle' });

与 scroll 示例的差异在于target是任意元素而非 body,Tether 会为它计算一个替代滚动条.pointer(3.6em 高、与容器等宽),垂直位置严格映射该容器scrollTop / (scrollHeight - clientHeight)的进度。示例后续代码(高亮当前句)只是演示性效果,与 Tether 无关。这展示了scroll-handle修饰器可以驱动“自定义滚动条、阅读进度条、章节导航”一类组件。

dolls:60 个元素的链式性能演示

dolls/index.html 配合 examples/dolls/dolls.js 是一个压力/性能演示:脚本创建 60 个div追加进.scroll容器,每个元素 tether 到前一个元素,形成一条链:

var count = 60; var parent = null; var dir = 'left'; while (count--){ var el = document.createElement('div'); el.setAttribute('data-index', count); document.querySelector('.scroll').appendChild(el); if (count % 10 === 0) dir = dir == 'right' ? 'left' : 'right'; if (parent){ tethers.push(new Tether({ element: el, target: parent, attachment: 'middle ' + dir, targetOffset: (dir == 'left' ? '10px 10px' : '10px -10px') })); } parent = el; }

可以看到链的走向每 10 个元素左右折返一次(dir在 left/right 间切换),targetOffset给出 10px 的间隙。演示的交互是“拖动左上角第一个元素”:dolls.js中监听mousedown/mousemove更新被拖元素的top/left,随后调用静态方法Tether.position()一次性重算全部实例——这是链式布局的关键,否则只有第一个 Tether 更新而其余 59 个滞后。这个示例直观验证了 Tether 批量实例下的重定位行为。

示例的自动化验证

文档目录中未列出、但对“示例可长期运行”至关重要的一点:这些示例有专门的端到端测试。test/cypress/integration/ 下存在simple.cy.js、enable-disable.cy.js、pin.cy.js、scroll.cy.js、content-visible.cy.js、testbed.cy.js等用例,配合package.json中的test:cy:ci(先pnpm build,再起http-server -p 9002,最后cypress run)形成闭环——也就是说每个 Demo 的行为变更都会被回归测试捕获。本地想打开示例时,流程为:pnpm build生成dist/,再在仓库根目录起任意静态服务器访问examples/<示例名>/index.html。

小结:从示例到配置的选择路径

综合这 9 个 Demo,可以把 Tether 的定位能力归纳为一张“场景 → 参数”速查表:

场景关键参数
基础气泡/工具提示attachment+targetAttachment+constraints: [{ to: 'window', attachment: 'together' }]
放不下就隐藏同上约束,靠默认类tether-out-of-bounds控制显示
永不越出窗口约束中加pin: true(等价四边钉住)
动态开关实例方法tether.enable()/tether.disable()+tether.enabled标志
对齐可见区targetModifier: 'visible'(或target: 'viewport'简写)
跟随滚动条/进度targetModifier: 'scroll-handle',target 可为 body 或任意可滚动元素

文档结尾也明确欢迎社区贡献新示例(“please send a PR with any examples you might create”)。结合上表与 examples/ 下每个目录“一个 index.html 自包含”的组织方式,扩展一个新 Demo 的成本很低:新建目录、写一个引用../../dist/js/tether.js的页面,需要时再补一个 Cypress 用例即可。

  • 前端
  • UI组件

【免费下载链接】tether

A positioning engine to make overlays, tooltips and dropdowns better

项目地址:https://gitcode.com/gh_mirrors/te/tether
点击查看免费下载
上一篇:Windows 11终极优化指南:使用Win11Debloat一键提升70%系统性能
下一篇:NoteDiscovery企业级部署指南:5分钟实现智能知识库容器化配置

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询