前十一式的招式,大多一句话就能点完;这一式我们回头看两件容易被忽略的内功—— 链式调用的“刚才那一步在哪”,和自己动手去量网页元素的尺寸与位置。

本式要点
  • 链式调用为什么能一路点下去,以及适时换行的写法
  • .end() 回到上一环、.addBack() 把上一环并回来
  • 量尺寸:width / height / innerWidth / outerWidth
  • 定位置:offset() / position() / scrollTop()
  • .data():把数据挂在元素上,刷新前一直存在

链式调用:一气呵成

jQuery 的绝大多数方法都返回自己(那个 jQuery 对象),所以可以一路点下去。 就像一句话说到一半不停顿:“找到卡片 → 给它一条边 → 里面的字变色 → 再淡入”。 写的时候别怕换行:一行一个动作,读起来比挤成一长条清楚得多。

.end() 的作用就在注释里:把“操作对象”退回一步。 没有它,第三步之后你就只能继续操作段落;有了它,第四步又能回到容器身上。 这在你不得不在“父子元素之间来回切换”时特别省事。

例 1 · .end() 前后对照

不加 .end():边框落在段落上,容器保持原样

我是段落

加了 .end():页面先给段落上色,再退回来给容器加边

我是段落

还有一个亲兄弟 .addBack():它不是“退回”,而是“把上一环也叫回来一起操作”。 典型场景:$('#box').find('.item').addBack() 得到的是“容器 + 它里面的 item”, 适合做“父容器与子元素一起”的一次性处理。

量尺寸:width 与 height 家族

元素的“宽度”其实有好几层含义,jQuery 把它们分别做成方法,量的是哪一层一眼便知:

方法量的范围
width() / height()内容区(不含内边距、边框、外边距)
innerWidth() / innerHeight()内容区 + 内边距
outerWidth() / outerHeight()内容区 + 内边距 + 边框
outerWidth(true)连外边距也算上

传数字进去就是设置尺寸,例如 $('#box').width(300); 不传参数就是读取当前计算值(读回来的都是像素数字,不带单位)。

定位置:offset / position / scrollTop

  • offset()——元素相对于文档左上角的位置,返回 {top: …, left: …};
  • position()——元素相对于最近的定位祖先的位置(“我被摆在哪”);
  • scrollTop()——滚动条往下滚了多少像素;传 0 就滚回顶部。
例 2 · 量一量:尺寸、位置与滚动
测量对象

第一行:往下滚,看看读数怎么变。

第二行:scrollTop 会告诉你答案。

第三行:它统计的是“向上滚出视野”的像素。

第四行:滚到底再点按钮看看。

第五行:路过人间。

第六行:终点线在下面。

第七行:要滚到底了。

第八行:到头了。

点上面的按钮,读数显示在这里。

数据缓存:.data()

.data(key, value) 把一小份数据挂在元素上,读的时候 .data(key) 取回, .removeData(key) 删除。它和 attr('data-…') 的区别是: .data() 存的是真正的 JavaScript 值(数字、数组、对象都行), 不会写进 HTML,也不怕被序列化成字符串——适合放“临时状态”: 点了几次、选中了哪一项、上一次的值是什么。

例 3 · 用 .data() 记一个“点了几次”

已经点了 0 次;元素身上挂着的数据是 0。

心法:.data() 与 .attr('data-…') 各管一段

页面初始值写在 HTML 的 data-* 属性里,用 .attr() 读; 程序运行起来后的临时状态用 .data() 存。 两者同名时会互相“偷看”(首次 .data('k') 会去读 data-k 属性), 但之后各存各的——同一份数据只用一个来源,才不会自己跟自己打架。

练功 · 第十二式
  1. 用一条链式完成:给 #jq12-end 里的段落加 fw-bold,退回来给容器加 border,再给容器追加一行小字。
  2. 做一个“回到顶部”按钮:点击时让页面的滚动条回到顶部(提示:窗口的滚动位置用 $(window).scrollTop())。
  3. 思考题:链式调用越长越好吗?什么时候应该拆成几段写?
参考答案 1

end() 的落点要看清楚:它把操作对象从“段落”退回“容器”; 之后的操作都在容器上,直到链条结束。写成多行、对齐缩进,读起来像一段小剧本。

参考答案 2

同时写 html 与 body 是历史遗留的习惯——不同浏览器把“页面滚动条” 挂在不同的元素上,两个都写就不会扑空。animate({ scrollTop: 0 }) 比直接 scrollTop(0) 多了一段平滑滚动的过程。

参考答案 3

不是越长越好。链式的好处是表达“同一批元素的连续动作”; 一旦中途换了主语、或者中间需要判断(if)、需要复用某个中间结果, 就该果断断开赋值给变量。还有一个现实因素:链式过长,出错时定位困难—— 报错行号只能告诉你“这一长条里有问题”,却不告诉你是哪一环。 心法一句话:一气呵成的写成链,需要喘气的就另起一行。

本式小结

  • 链式调用靠“方法返回自己”串起来,一行一个动作、对齐缩进最易读;
  • .end() 退回上一环,.addBack() 把上一环并进来;
  • 尺寸四层:width(内容)→ innerWidth(含内边距)→ outerWidth(含边框)→ outerWidth(true)(含外边距);
  • offset() 相对文档、position() 相对定位祖先、scrollTop() 管滚动;
  • .data() 存运行时状态,data-* 属性存初始值,一个数据一个来源。
下一式,我们不再满足于用别人的招式——亲手造一个插件。