jQuery 1.4在本月早些时候发布了。这个版本包含了大量的编程,测试和记录文档的工作。这份官方文档总结了jQuery 1.4当中加入的变更和功能,很多常用和热门的方法被重写,性能得到了很大的提升。 本文来自ajax之家,为jQuery 1.4官方文档中文版的发布公告。jQuery 1.4在本月中旬发布。 为了庆祝jQuery的四周岁生日, jQuery的团队荣幸的发布了jQuery Javascript库的最新主要版本! 这个版本包含了大量的编程,测试,和记录文档的工作,我们为此感到很骄傲。 我要以个人的名义感谢 Brandon Aaron, Ben Alman, Louis-Rémi Babe, Ariel Flesler, Paul Irish, Robert Kati?, Yehuda Katz, Dave Methvin, Justin Meyer, Karl Swedberg, and Aaron Quint。谢谢他们在修复BUG和完成这次发布上所做的工作。 下载(Downloading) 按照惯例,我们提供了两份jQuery的拷贝,一份是最小化的(我们现在采用Google Closure作为默认的压缩工具了),一份是未压缩的(供纠错或阅读)。 jQuery压缩 (23kb Gzipped) http://code.jquery.com/jquery-1.4.min.js jQuery常规 (154kb) http://code.jquery.com/jquery-1.4.js 另外,Google也在他们的服务器上放置了一份jQuery的拷贝。这份拷贝会自动的最小化然后压缩 – 并且放在Google最快的缓存服务器上。 http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js 你可以在你的站点上直接引用上面的URL,这样就可以享受迅速加载jQuery的性能优势了。 就jQuery1.4来说,我们努力的减少大规模升级中的麻烦 – 通过保持所有public函数的签名。即使如此,还请通读可能会造成问题的变更列表,这样能够了解哪些变更可能会给你的应用造成问题。 (功能) Features 下面的内容概括了jQuery1.4里加入的变更和功能。另外所有的变更都已经在jQuery 1.4 的文档里记录了。 热门方法经过了性能上的大”检修” 不少比较热门的和常用的jQuery方法在1.4里被重写了。(译注:重写了方法的内部,外部调用没有大幅度改变) 我们分析源码的时候发现我们能够获得大幅的性能提升,通过把jQuery和自己比较: 查看内部函数被调用了多少次,然后努力降低源码的复杂度(译注:计算机算法中的Complexity) View the cropped chart. 在1.4版里我们显著的降低了大部分热门jQuery方法的的复杂度。 更易用的设置函数 (Easy Setter Functions) 算来已经有一阵了,你们已经可以给.attr()传递一个函数,然后这个函数的结果会被用来赋给相应的HTML属性(attribute)上。这个功能现在被移植到所有的设置函数了: .css(), .attr(), .val(), .html(), .text(), .append(), .prepend(),.before(), .after(), .replaceWith(), .wrap(), .wrapInner(), .offset(), .addClass(), .removeClass(), 以及 .toggleClass(). 另外, 对于下面几个方法,当前的值会被作为第2个变量传递给这个函数。.css(), .attr(), .val(), .html(), .text(), .append(),.prepend(), .offset(), .addClass(), .removeClass(), 以及 .toggleClass(). 这样代码就可以这样写: // 找出所有A标签里的'&'字符,然后用一个span标签包围 $('a').html(function(i,html){ return html.replace(/&/gi,'&'); }); // 给一些链接的title属性加些信息 $('a[target]').attr("title", function(i,title){ return title + " (新窗口打开)"; }); Ajax 嵌套参数的序列化 (jQuery.param() 文档, Commit 1, Commit 2) jQuery 1.4在jQuery.param方法里加入了嵌入参数序列化的支持,借用了PHP编程里兴起的,而后又被Ruby on Rails推广开来的方式。 举例来说, {foo: ["bar", "baz"]} 会被序列化为 “foo[]=bar&foo[]=baz”. 在jQuery 1.3版里, {foo: ["bar", "baz"]} 曾被序列化为 “foo=bar&foo=baz”. 但是,这样做没用办法将只含有一个元素的阵列编码。如果你需要旧的序列化方式,你可以设置传统Ajax设置来进行切换。(使用jQuery.ajaxSettings.traditional进行全局切换,或者根据情况单独切换。 总共有3种方式可以切换到旧的序列化方式: // 全局改变序列化方式 (使用旧的) jQuery.ajaxSettings.traditional = true; // 指定情况使用旧的序列化方式 jQuery.param( stuff, true ); // 针对一个单独的Ajax请求使用旧的序列化方式 $.ajax({ data: stuff, traditional: true }); 更多信息参见: jQuery.param() 文档, jQuery.ajax() 文档, Commit, Code JSON和脚本类型通过”content-type”自动识别。 (jQuery.ajax 文档, Commit 1, Commit 2) 如果一个Ajax请求的回复的媒体类型是JSON(application/json), dataType默认设为”json”(如果dataType没有被指明)。另外,如果回复的媒体类型是 Javascript(application/javascript), dataType默认设为”script”(同样,如果dataType没有明确指明), 这种情况下,脚本会自动运行。 加入了Etag的支持 (jQuery.ajax() 文档, Commit) 默认设置下, jQuery会忽略Ajax请求的”Last-Modified”页头。这样做是为了忽略浏览器的缓存。设置ifModified:true就可以使 jQuery使用可用的缓存。jQuery1.4还会发出”If-None-Match”的页头如果你设置了ifModified选项。 严格JSON模式,本地的JSON.parse方法 (jQuery.ajax() 文档, Commit 1, Commit 2, Commit 3) jQuery 1.3和以前的版本曾使用Javascript的eval对引入的JSON解析。1.4版则会使用本地的JSON解析器,前提是如果有本地的解析器可用。它也会对引入的JSON进行校验。所以在jQuery.getJSON方法里,或当一个Ajax请求的dataType是”json”的时候,jQuery会拒绝不合标准的JSON(例如{foo: "bar"})。 序列化HTML5的元素 (jQuery.param() 文档, Commit) 新的HTML5输入方法 (比如’datetime’和’range’)在序列化.serialize()一个表单的时候会被包括在内。 Ajax请求的环境 (jQuery.ajax() 文档, Commit) 你可以附加一个”环境”到Ajax请求上,所有的回调函数里都会拥有同样的”环境”设置(这样可以简化你的代码,尽可能避免使用闭合,或是其他对象)。 jQuery.ajax({ url: "test.html", context: document.body, success: function(){ jQuery(this).addClass("done"); } }); 请求成功回调函数的第三个参数会被设为原始的XHR对象 (jQuery.ajax() 文档, Commit) 所有的Ajax请求的成功回调函数现在都会收到原始的XMLHttpRequest对象,作为第三个参数。之前这个XHR对象只能通过$.ajax一类方法的返回值来获取。 明确设置”Content-Type” (jQuery.ajax() 文档, Commit) 在1.3版,如果没有实际数据发送,jQuery.ajax的contentType会被忽略。1.4版里,contentType将总是和请求一同发送。这修复了某些后台凭靠”Content-Type”页头判断回复类别所造成的问题。 明确设置JSONP回调函数的名字 (jQuery.ajax 文档, Commit) 你可以使用jQuery.ajax()方法的jsonpCallback选项,通过名字来指定JSONP的回调函数。 防止启动前跨域XHR (Commit) 跨域Ajax(针对提供支持的浏览器)将更易用,因为默认设置下,启动前XHR被阻止了。(TODO) jQuery.ajax()现在使用”onreadystatechange”事件替换了计时器 (Commit) 使用”onreadystatechange”替换了轮流探询,Ajax请求现在将使用更少的资源 元素属性 (Attributes) .css()和.attr() 的性能被优化了。 < .attr()方法多了一个设置函数作为参数 (.attr() 文档) 你不但可以将一个函数用在.attr()里,还可以在这个函数里使用属性的当前值。 jQuery('
') .attr("alt", function(index, value) { return "Please, " + value; }); .val( Function ) (.val() 文档)
jQuery("input:text.food").hide(); jQuery("
" ) .sortable() .bind("endsort", function() { $(":text.food").val(function() { return $("ul.sortable li:eq(" + $(this).attr("data-index") + ")").text(); }); }); text和CDATAHTML元素也支持.text()方法了 (.text() 文档, Commit) 核心 (Core) 快捷元素创建 (jQuery() 文档, Commit) 现在当你需要使用jQuery函数创建一个元素的时候,你可以同时附递一个对象来指定属性值和事件: jQuery(" ", { id: "foo", css: { height: "50px", "50px", color: "blue", backgroundColor: "#ccc" }, click: function() { $(this).css("backgroundColor", "red"); } }).appendTo("body"); 对象里的键值的名字与相关的jQuery的方法的名字是对应的,对象的值会被作为参数传递给jQuery的方法。 (译注:譬如$("
link", {css:{background:"#ccc"}});相当于$("
link")).css("background", "#ccc"); .eq(-N), .get(-N) (负指数) (.eq() 文档, .get() 文档, Commit) 你现在可以在.get()和.eq()方法里使用负数。譬如,你要选择倒数第2个div元素,或者是倒数第2个DOM对象: $("div").eq(-2); $("div").get(-2); 新的.first()和.last()方法 (.first() 文档, .last() 文档, Commit) 方便起见, 新增的.first()和.last()方法等同于.eq(0)和.eq(-1). 新的.toArray()方法 (.toArray() 文档, Commit) .get()方法自始就是从jQuery集合里返回一个阵列。为了能够更明确, 你可以用.toArray()来达到一样的效果。(译注:这里应该是为了以后的版本留出空间,譬如以后可能会加入.toList()方法,到时候就会易于区分。) 不过,和.get()不一样的是,.toArray()不接受参数。 jQuery()返回一个空集 (jQuery() 文档, Commit) 在jQuery 1.3中,jQuery()方法返回仅包括document的jQuery集合。这个可以用来创建一个空集,然后动态加入一些元素。注: jQuery().ready()方式在1.4中依然有效,但是被指示陈旧了。请使用jQuery(document).ready()或者jQuery(function(){})。 jQuery(“TAG”) (Element Selector 文档, Commit) 当使用单个标签名字的时候jQuery会使用更快捷的路径。 jQuery(“
”), jQuery(“
”) 和 jQuery(“
”) (jQuery() 文档, Commit) 现在这三个方法都使用同一个代码路径了(document.createElement), 来优化jQuery("
")的性能。注意,如果你指定了属性,将会使用浏览器本身的语法分析(通过设置innerHTML)。 样式 (CSS) .css()方法在性能是以前的2倍。 .addClass(), .removeClass(), 和 .hasClass()这几个方法在性能上是以前的3倍 .toggleClass()可以切换多个css类了 (.toggleClass() 文档, Commit) 你可以通过.toggleClass()调用多个css类的名字来切换他们。 $("div").toggleClass("current active"); 数据 .data()返回对象, .data(Object)设置对象 (.data() 文档, Commit 1, Commit 2) 有时候你可能需要在一个元素上附加一个复杂的对象。一个常见的例子是你需要从一个元素身上复制所有的数据到令一个元素上。在jQuery 1.4里, 不使用任何参数调用.data()时,.data会返回一个复杂对象。(译注: 包含所有键-值对的对象。) 调用.data(Object) 则会设置这个对象。注意这个对象还包括了元素上绑定的事件,所以用的时候要小心。 除非需要, 不然不会创建数据缓存。 (Commit 1, Commit 2, Commit 3) jQuery使用一个独特的自定义属性来获取特定元素上附加的数据。当查找数据,但是没有新加的数据的时候,jQuery会尽量避免创建这个自定义属性。这样可能会提高性能,同时还会在这种情况下避免污染DOM。 效果 (Effects) 单个属性缓进缓出 (Per-property Easing 文档, Commit) 除了能够给一个动态效果指定缓进出函数外,你现在可以指定每个属性的缓进出函数了。James Padolsey的blog上有更进一步的信息和演示。 $("#clickme").click(function() { $("div").animate({ ["+=200px", "swing"], height: ["+=50px", "linear"], }, 2000, function() { $(this).after("
Animation complete.
"); }); }); 事件 (Events) 新方法: jQuery.proxy() (jQuery.proxy() Documenation, Commit 1, Commit 2) 如果你需要保证一个函数内的”this”恒定地保持某个值, 你可以用jQuery.proxy获得一个相同作用域的函数。 var obj = { name: "John", test: function() { alert( this.name ); $("#test").unbind("click", obj.test); } }; $("#test").click( jQuery.proxy( obj, "test" ) ); 多个事件绑定 (.bind() 文档) 你可以通过递入一个对象来一次性绑定元素的多个事件。 $("div.test").bind({ click: function(){ $(this).addClass("active"); }, mouseenter: function(){ $(this).addClass("inside"); }, mouseleave: function(){ $(this).removeClass("inside"); } }); ‘change’和’submit’事件规范化 (Change 文档, Submit 文档) 普通的或是即时的change和submit事件可以在各种浏览器上稳定工作了。我们覆盖了IE里的change和submit, 替换为与其他浏览器相同的事件。 新的事件: ‘focusin’ and ‘focusout’ (.focusin() 文档, .focusout() 文档, Commit) focusin和focusout在一般情况下等同于focus和blur, 但是多了向父元素传递的作用。如果你自己编写你的事件代理模式(TODO), 这个功能将对你有很大帮助。请注意对focus和blur使用live()方法将不会起作用; 在设计的时候我们根据DOM事件规范决定不使其向父元素传递事件。 $("form").focusout(function(event) { var tgt = event.target; if (tgt.nodeName == "INPUT" && !tgt.value) { $(tgt).after("nothing here"); } }); 所有的事件都可以成为即时事件 (.live() 文档) 除了ready, focus (用focusin), 和 blur (用focusout)以外, 所有能用.bind()绑定的事件都可以成为即时事件。 在live()所支持的事件里,我们对能够支持下面这几个额外的事件感到尤其骄傲。通过.live()里的事件代理, 1.4版实现了对change, submit, focusin, focusout, mouseenter, 以及mouseleave事件的跨浏览器支持。 注: 如果你需要即时的focus事件,你应该用focusin和focusout, 而不要用focus和blur, 因为就像前面提到的,focus和blur不向上传递。 还有, live()也接受数据对象作为参数了, 同bind()方法一样 (Commit) live/die也支持环境变量了 (Commit) 现在可以在绑定事件的时候给选择符指定一个环境。如果环境被指定了, 只有属于这个环境下的元素才会被绑定事件。在创建即时事件的时候, 元素本身不需要已经被定义, 但是环境必须被创建。 确定ready事件至少含有body元素 (Commit) jQuery现在会检查body是不是存在,如果不存在,会对body进行轮流探询。 在不需要手动处理内存溢出的非IE浏览器中, 卸载的速度提高了。 (Commit) DOM操作 (Manipulation) 在jQuery 1.4里一系列的DOM操作方法的性能都有巨大的提升。 .append(), .prepend(), .before(), and .after()的性能提高了。 .html()的性能提高到以前的3倍。 .remove()和.empty()的速度则达到以前的4倍. 新方法: .detach() (.detach() 文档, Commit) detach()将一个元素从DOM里移除, 但是并不卸载关联的事件处理函数。这个方法可用于暂时性的将一个元素移除,执行相关操作,然后返回。 var foo = $("#foo").click(function() { // 相关操作 }); foo.detach(); // foo保留了相关处理函数 foo.appendTo("body"); 新的unwrap()方法 (documentation, commit) unwrap()方法拿到一个已知的父元素的子元素,然后将父元素用子元素替换。(译注: 将子元素从”包裹”里拿出来, 因名unwrap)。如此这般:
$('div').unwrap();
annie
davey
stevie
domManip方法里的缓存 (commit) jQuery会将jQuery("
")和.after("
")一类方法创建的节点记入缓存。这样, 对于利用这些方法, 使用字符串进行DOM操作的页面,性能将有极大的提高。 无连接的节点间的before, after, replaceWith操作 (commit) 现在你可以对还没有放置到DOM Tree上的节点进行before, after, 和replaceWith的操作了。意味着你可以先对节点进行复杂的操作, 待完成后再放到合适的DOM位置上。这样也能尽量避免操作过程中造成重新排版。 jQuery("
").before("
Hello
").appendTo("body") .clone(true) 也会复制关联数据 (clone 文档, commit) 1.3版中, .clone(true)虽然也是深度复制, 但是没有复制关联的数据。1.4版里,它则会复制数据, 同时还包括所有的事件。这点上和jQuery.extend在语义想同的, 所以普通对象和阵列会被复制, 但是自定义的对象则不会。 位移 (Offset) .offset( coords | Function ) (.offset() 文档, commit) 现在可以设置元素的位移了! 和所有的设置函数一样, offset也可以接受一个函数作为第二个参数。 队列 (Queueing) 队列经历了一次大修, 使用队列会比使用默认的fx更易掌握。 新的 .delay() 方法 (.delay() 文档, commit) .delay()方法会根据参数滞后若干毫秒执行队列里剩下的对象。默认的它会使用”fx”队列。但你可以选择性的通过delay方法的第二个参数选择其他队列。(译注:每个队列都以一个名字识别。) $("div").fadeIn().delay(4000).fadeOut(); 队列里的next (.queue() 文档, commit) jQuery 1.4版里, 当队列里的一个函数被调用的时候,第一个参数会被设为另一个函数。当后者被调用的时候, 会自动排除队列里的下一个对象, 以此来推动队列到下一步。 jQuery("div").queue("ajax", function(next) { var self = this; jQuery.getJSON("/update", function(json) { $(self).html(json.text); next(); }; }).queue("ajax", function() { $(this).fadeIn(); }); .clearQueue() (clearQueue 文档, commit) 队列可以被清空了。这个方法会移除队列里所有未执行的函数, 但不会移除正在运行的函数。无参数的情况下调用.clearQueue()方法将会清空默认的”fx”队列。 选择符 (Selectors) “#id p”效率更高 (commit) 所有以ID开头的选择符都得到了优化, 能够在瞬间得到返回值。所有以ID为开头的选择符速度将一直快于其他选择符。 页面遍访 (Traversing) .index(), .index(String) (index 文档, commit) .index() 方法经过重写, 变得更加直观和灵活。 你可以获得一个元素相对于同父元素的指数: // 计算第一个
元素在它所有的同父元素中的指数: $("li.current").index() 你也可以获得一个元素在一个jQuery元素集合中的指数, 这个集合可以用一个选择符或者是一个DOM元素来指定: // 计算这个 元素在页面上所有
元素里的指数: $("#more-info").index("h3") 新的.has()方法 (has 文档, commit) 这个方法相当于选择符里的:has()过滤法。它拿到一个jQuery集合,返回含有指定选择符的元素。 新的 .nextUntil(), .prevUntil(), .parentsUntil() 方法 (.nextUntil() 文档, .prevUntil() 文档, .parentsUntil() 文档,commit) 新的”until”方法类似于.nextAll(), .prevAll(), 和.parents()。区别是可以用一个选择符来停止元素探索。 .add(String, Element) (.add() 文档, commit) 可以给.add()方法指定环境了。这个功能可以用于在一个调用链中加入和操作额外元素(比如Ajax请求里返回的新元素)。 .closest(filter, DOMElement) (.closest() 文档, commit) 可以通过closest方法的第2个参数设置一个DOMElement环境。给closest设置一个环境一般能够提高这个方法的运行速度。这个优化也适用live(), 因为这个方法内部调用了closest()。 常用工具 (Utilities) jQuery.isEmptyObject() (jQuery.isEmptyObject() 文档, commit) 如果对象,em>没有任何属性, 该方法将返回true。jQuery.isEmptyObject()方法不对参数进行任何检查, 所以请保证参数是一个对象。 jQuery.isPlainObject() (jQuery.isPlainObject(), commit ) 如果一个对象是通过字符创建的(译注:{}),jQuery.isPlainObject()返回true; 如果对象是其他类别的对象(译注:如new Object())或者是基本类型, 则返回false。 jQuery.contains() (jQuery.contains() 文档, commit) 如果两个参数都是DOM节点,并且第二个节点是嵌套在第一个节点内部的话, jQuery.contains()返回true。反之返回false。 jQuery.noop (jQuery.noop() 文档, commit) 是个空的函数, 可以用在必须要有一个函数的情况下。(译注: noop是No Operation的意思。) jQuery.unique() (jQuery.unique() 文档) jQuery 1.4版中, jQuery.unique()方法返回结果里的元素是按照他们在页面里的顺序排序的。由于在创建jQuery集合的时候jQuery使用jQuery.unique()方法, 所以jQuery方法返回的集合也是按照他们在页面里的顺序排列的。 其他 (Miscellaneous) jQuery.browser以浏览器引擎为中心 (jQuery.browser 文档, commit) 例如, 你可以通过jQuery.browser.webkit探测引擎是否是Webkit。 改进了对applets的处理 (commit 1, commit 2) jQuery不再试图在Java applets上绑定事件或是数据了(绑定事件或是数据会出现错误)。 不再使用arguments.callee (commit) 为了顺应Caja的要求, 同时也因为即将开始应用的ECMAScript 5规范里将其标记为陈旧, 我们将jQuery核心中所有用到arguments.callee的代码都移除了。 用Closure Compiler替换了YUI Min (commit) 中文API文档支持 您可以在Ajax之家(http://www.ajaxa.cn/)下载最新的API文档 内部重组 (Internal Reorganization) 在1.4版的开发过程中的一个重点是要建立一个更易读, 更易懂的代码库。为了达到这个目标我们树立了一系列编写代码规范的向导。 下面是一些主要的变化: 旧的’core.js’文件被分成了’attribute.js’, ‘css.js’, ‘data.js’, ‘manipulation.js’, ‘traversing.js’, and ‘queue.js’. ready事件被移入了’core.js’ (因为它是jQuery的一个基本组成之一)。 大部分核心代码都符合新的代码规范. css和属性的逻辑被划分开来, 不再如以往相互缠绕。 测试 (Testing) 编程人员对于JQuery框架性能评论说明 定义JQuery.prototype函数改变函数说明 几分钟教您扩展Jquery的Json技巧说明 阐述Jquery工具函数的使用和解决方案 jQuery 1.4发布 代码库内部重写组织 jQuery 1.4版发布过程中我们修复了207个问题 (比较之下1.3版里有97个修复)。 jQuery 1.4.此外, 测试的数量从jQuery 1.3.2中的1504例升到了1.4中的3060例。 所有测试都在主要浏览器里完全通过了。(Safari 3.2, Safari 4, Firefox 2, Firefox 3, Firefox 3.5, IE 6, IE 7, IE 8, Opera 10.10, and Chrome) 我们尽量试图减小jQuery 1.4对大规模升级可能造成的麻烦 – 保持所有公开函数的签名不变。即使如此, 请通读下面的列表以保证你对可能对你的应用造成问题的变更。 .add()不再简单的将结果串联到一起, 结果将会被混合到一起, 然后根据他们在页面里的顺序排列。 .clone(true)将复制事件和数据, 而不仅是事件。 jQuery.data(elem) 不再返回id, 取而代之的是元素的对象缓存。 jQuery() (无参数) 不再自动转换成jQuery(document)了。 通过.val(“…”)获得一个option或一个checkbox的值不再有歧义(将总是根据value属性选择, 而不是根据text的值)。(Commit) jQuery.browser.version现在将返回引擎的版本. 现在起将对引入的JSON更严格, 如果JSON的格式不符将会报错。如果你需要对不符合JSON严格格式的Javascript进行估值, 你必须设置请求的文件类型为纯文本, 然后用eval()来对内容估值。 参数序列化默认会按照PHP/Rails的风格进行。你可以通过jQuery.ajaxSettings.traditional = true;来切换到旧的序列化方式。你也可以针对个别请求进行切换, 在调用jQuery.ajax的时候递入{traditional: true} 内部的jQuery.className被移除了。 jQuery.extend(true, …)不再扩展复杂对象或是阵列。(TODO) 如果一个Ajax请求没有指定dataType, 而返回的数据类型是”text/javascript”, 那么回复将会被执行。之前, 必须明确的指定dataType。 设置Ajax 请求的”ifModified”属性会将ETags纳入考虑。 我们还针对1.4版中可能造成问题的变更编写了一个向后兼容的插件。如果你升级到1.4以后出现问题, 可以在引入1.4版的文件之后引入这个插件。 如何使用这个插件: 原始数据和测试页面 性能测试中我们使用了下列测试套包: Attributes Class DOM Manipulation Empty/Remove Function Call Profiling: 1.3.2 1.4. 结果的原始数据 (所有的数据都是 1.3.2 vs. 1.4): 函数调用的次数 547 3 760 3 500 200 896 399 23909 299 307 118 28955 100 28648 201 1662 593 DOM嵌入 558 317 1079 624 1079 516 1155 829 436 332 196 194 243 169 HTML 116 46 281 78 313 78 234 63 134 43 43 42 91 27 CSS/属性 703 370 1780 1250 1765 1250 1157 749 629 498 346 184 333 161 CSS 114 52 203 93 118 93 109 47 116 54 58 24 54 22 CSS类 553 138 1578 546 1515 501 1033 327 769 298 229 80 173 41 移除/清空 3298 286 9030 2344 7921 1703 5282 1266 2898 303 1166 140 1034 122