传统的 jQuery 使用十分方便友好,但随着 ES6、HTML5/CSS3 的逐步成熟,我们可以编写一个轻量级的类 jQuery 工具,这就是 tQuery。它采用 ES6 语法编写,仅包含 jQuery 中有关 DOM、CSS 和 Event 的部分,即省略了 Ajax、Deferred 和 Effect 等,因为这些部分可由 ES6/HTML5 中原生的 Fetch、Promise 和 CSS3 来支持。
tQuery 名称里的 t 来源于 Tpb 里的 Template,但也可以认为是 tiny。
注:
本设计的接口虽然大部分与jQuery相似,但并不兼容,同名的接口只是在功能上相似而已。
可以打开test.html在浏览器控制台执行console.dir($)或console.dir($().__proto__)简单地查看接口分布情况。
jQuery 中对检索结果集的操作,在这里都有一个单元素的版本,如:$.next(el, slr)、$.hasClass(el, names) ,以及相应的集合版本:$('xx').next(...)、$('xx').hasClass(...)。单元素版如果没有值需要返回,通常返回 $ 自身,集合版会返回一个值集合或一个新的 Collector 实例。与 jQuery 中类似,集合版的 Collector 返回值可以链式调用。
与 jQuery 相比,大多数同名接口的行为相似,但不少地方依然区别明显。如对元素集的属性取值,jQuery 中仅是对集合内首个成员取值,如 $('a').attr('href') 取首个 <a> 元素的 href 属性值,而 tQuery 中则会返回集合里全部元素的属性值(一个数组)。
又如 $.html 方法,jQuery 中它既可以处理文本,也处理节点元素,效果与连续的 $.empty().append() 调用相似。但在 tQuery 中该接口只负责文本的逻辑:设置元素内容时接收HTML文本或提取来源元素的 outerHTML 值,获取源码时提取元素的 innerHTML 值。另外也支持插入位置和HTML编码功能。
在实现上,代码不对浏览器的原生事件对象产生侵入,元素上也不存储任何数据(因此没有 .data() 方法)。设计上尽量精简,只要有可能就把任务交给原生的 ES6、HTML5/CSS3 去处理。
下面接口的名称中,前置 $. 的指 $ 的成员,前置 . 表示 $(...) 检索结果(Collector 实例)的成员。
-
.contains()- jQuery: 仅针对内部子孙元素,不含容器元素自身测试。
- tQuery: 包含容器元素自身测试,与DOM同名接口行为相同。
-
.hasClass()- jQuery: 对空格分隔的名称序列视为一个整体。
- tQuery: 空格分隔的名称序列被视为多个名称,逐个判断,因此
class="A B"与class="B A"是一样的。
-
.is()- jQuery: 只要集合中有一个匹配即为true,且可接受多种类型的参数。
- tQuery: 对集合中每一个元素判断,返回一个true和false值混合的集合(数组),参数类型较为简单。
-
.first()/.last()- jQuery: 简单返回集合中首个或最后一个成员。
- tQuery: 返回集合中首个或最后一个匹配的成员。接受一个可选的选择器匹配参数,无实参调用时与jQuery相同。
-
.next()/.prev()- jQuery: 简单返回下一个或前一个兄弟元素,可以传递一个选择器测试是否匹配,不匹配则返回
null。 - tQuery: 返回下一个或前一个匹配的兄弟元素,如果未传入匹配测试条件,则无条件匹配(同jQuery)。匹配测试支持函数(函数支持迭代计数)。
- jQuery: 简单返回下一个或前一个兄弟元素,可以传递一个选择器测试是否匹配,不匹配则返回
-
$.data()/.data()- jQuery: 存储与目标元素关联的任意数据,或者返回集合中匹配首个元素的存储的值。
- tQuery: 没有该接口。元素的data-xx系属性融入在
attr和prop接口中,支持名称简写(-xx表示data-xx)。
-
$.get()/.get()- jQuery:
$.get为用Ajax方式通过GET方法获取目标网站的内容。.get为获取集合内的某个成员。 - tQuery:
$.get为获取文档中单个元素的版本,如通过id定位或querySelector检索。.get为集合版的同功能接口,获取集合内某个成员采用.item接口。
- jQuery:
-
$.wrap()/.wrap()/$.wrapInner()/.wrapInner()- jQuery: 传递包含子元素的包裹容器时,会递进到最深层子元素为实际包裹容器,整个容器为复制方式。支持选择器选择目标元素。返回原始被包裹元素的集合。
- tQuery: 与jQuery相同,包含子元素的包裹容器会递进到最深层子元素为包裹元素,但容器元素的克隆是可选的,并且返回的是包裹容器(根)本身,同时也支持被包裹内容为文本。如果包裹元素是既有的元素,可以指定是否同时克隆元素(及其子孙元素)上绑定的事件处理器。另外,集合版支持包裹元素的数组式指定(与被包裹数据一一对应)和前值默认的功能。
-
.wrapAll()- jQuery: 用目标容器元素包裹集合内的全部成员,与
$.wrap()等接口类似,容器元素会递进到首个最深层子元素(也为克隆方式),返回原始被包裹内容的集合。 - tQuery: 用目标容器元素包裹集合内的全部成员,也与本库的
$.wrap()等接口类似,支持包裹元素的克隆选项,返回包裹容器本身(Collector封装),因此可以简单的链式调用(如.appendTo()等)。
- jQuery: 用目标容器元素包裹集合内的全部成员,与
-
$.html()/.html()- jQuery: 接收节点参数时,是执行简单的复制和移动,实参节点会从原来的地方脱离DOM树。数组实参会被合并为单一的值,然后拷贝赋值到各个目标。
- tQuery: 接收节点参数时,会提取元素的
outerHTML或文本节点的textContent,这里是纯粹的文本逻辑,不会影响原节点,同时也可以指定插入的位置。集合版版中数组实参是一一对应的逻辑。
-
$.text()/.text()- jQuery: 接收节点参数时,只是简单地将节点转换为字符串,因此一个元素实际上会被转换为
[object HTMLElement]之类的值。数组实参会被转换为数组的字符串表示,然后赋值。 - tQuery: 接收节点参数时,会取节点的
textContent值,不论这个节点是元素、文本节点还是注释节点。同样地,也可以指定插入的位置。集合版数组实参的说明参考.html接口说明。
- jQuery: 接收节点参数时,只是简单地将节点转换为字符串,因此一个元素实际上会被转换为
-
$.val()/.val()- jQuery: 获取或设置表单控件
value属性的值,不论该表单控件是否已disabled,效果与.prop('value', '...')相同。 - tQuery: 严格遵循表单逻辑:取值时返回
value属性的值,设置时部分控件为对比目标值并改变控件状态(如单/复选按钮、选单等),而非修改value属性本身。例如不能对<option>控件直接取值或设置,因为它们由<select>管理。另外,如果控件已disabled,则取值返回null,设置不起作用。(注:这是一个与 jQuery 版差异较大的接口)
- jQuery: 获取或设置表单控件
-
$.scroll()/.scroll()- jQuery: 是绑定滚动事件处理器的便捷方法。
- tQuery: 窗口/元素滚动条位置的获取或滚动到目标位置的执行(会触发事件)。注:绑定事件处理器仅采用
$.on()方法实施。
注: tQuery 只涉及 jQuery 中与 DOM 相关的部分,因此差异对比也仅限于这一子集。
-
元素和节点创建。
- jQuery: 通过
$( html )方式创建元素,但仅能创建元素(Element)而无法创建单纯的文本节点(Text)。 - tQuery: 通过
$.Element( tag, data, conf )创建元素,通过$.Text( data )创建文本节点。另外还有 SVG 和 Table 的特定接口。
- jQuery: 通过
-
数据集合对元素集合的赋值。
- jQuery: 当数据赋值到jQuery检索的结果集时,数据被视为一个单元执行拷贝赋值。如:
$('p').text([1, 3, 5]),数组被转化为一个字符串:1,3,5,然后拷贝赋值到集合中每一个段落元素。 - tQuery: 当目标元素是集合时(
Collector),部分接口中数据源的类型被区别对待:如果是一个数组,则是各自一一对应。如上述例子,集合中的前3个段落被分别填充为1、2、3等值。
- jQuery: 当数据赋值到jQuery检索的结果集时,数据被视为一个单元执行拷贝赋值。如:
-
before/after/append/prepend/html/text 等接口的功能限定。
- jQuery: 全部方法既支持节点/元素、也支持HTML字符串即时创建元素。
- tQuery:
before/after/append/prepend/replace/fill六个方法被限定为仅支持节点/元素,因此也就支持节点/元素的克隆,以及元素上绑定的事件处理器的克隆。html/text被严格限定为文本类操作,且支持插入位置参数和源码编解码的简单功能,条理更清晰一些。
-
事件委托绑定(
delegate)的触发区别。- jQuery: 事件处理器的绑定可以用一个选择器限定事件触发的目标。从事件的触发起始元素(
target)开始到委托绑定的元素,如果有多个目标元素匹配,就会触发多次事件处理器的调用。 - tQuery: 委托绑定的规则与jQuery相同,但事件的触发仅有一次,触发的目标是从起始元素(
target)开始(含target),向上第一个匹配的元素。注:从事件处理器接口(function(event, elo): Any)的第二个实参elo上取当前匹配元素(elo.current)。
- jQuery: 事件处理器的绑定可以用一个选择器限定事件触发的目标。从事件的触发起始元素(
-
节点变化事件(
varyevent, bindevent)。为了跟踪DOM节点的变化,设计加入了如下定制事件:
attrvary, attrdone元素特性设置:之前/完成事件。由.attr|.attribute|.removeAttr|.toggleAttr接口触发。propvary, propdone元素属性设置:之前/完成事件。由.prop|.property|.val接口触发。stylevary, styledone元素内联样式设置:之前/完成事件。由.css|.cssSets接口触发。classvary, classdone元素类名设置:之前/完成事件。由.addClass|.removeClass|.toggleClass接口触发。nodein, nodeok, nodesdone节点插入DOM:之前/完成/全部完成事件。由5个接口.prepend|.append|.before|.after|.replace触发。也可能由复合类操作如fill, wrap, wrapInner, html, text等触发。detach, detached节点脱离DOM:之前/完成事件。由接口.remove触发。empty, emptied节点内清空:之前/完成事件。由接口.empty触发。normalize, normalized节点规范化:之前/完成事件。由接口.normalize触发。
这对于需要跟踪DOM节点变化的应用,可以获得节点改变之前、完成或出错三个阶段的监听处理能力。事件冒泡且不可取消,附加的消息(
event.detail)包含了关联的数据。注:出错和完成事件不会同时发生,两者仅有其一。另外,对于在元素上绑定和解绑事件处理器也提供了如下通知事件:
bind, bound在元素上绑定事件处理器之前/后,适用tQuery.on()/one()接口。unbind, unbound在元素上解绑事件处理器之前/后,适用tQuery.off()接口。
节点变化事件的功能默认关闭,需要执行
$.config( {bindevent:true, varyevent:true} )开启。 -
泛节点接口。 tQuery 中加入了5个包含非空文本和注释节点的检索接口,以便更仔细地分辨节点位置。它们是:
prevNode/nextNode获取当前节点之前或之后的下一个节点,节点包含:元素、非空文本节点和可选的注释节点。如果迭代到末端依然没有条件符合,则返回null。prevNodes/nextNodes获取当前节点之前或之后的兄弟节点集,节点包含范围同上。siblingNodes获取当前节点同级的兄弟节点集,节点包含范围同上。
tQuery 也支持 Sizzle 中很有用的直接子元素选择器 >...。如:$('>b, >a', ctx),表示选取 ctx 内 <b> 和 <a> 直接子元素。
tQuery 可以脱离 Sizzle 使用,它采用了浏览器内置的 querySelector()/querySelectorAll() 通用方法,但该方法并不支持上面的选择器形式。实际上,tQuery 只是简单地用一个临时属性名补充到 > 之前,从而构造出一个合法的选择器(形如:P[___tquery_bcc6c0df_]>...)。
临时属性名由一个固定的前缀 ___tquery_ 和当前时间戳(16进制)以及一个结尾 _ 构成,以避免与应用冲突(您知道这点更好)。