jQuery DOM操作总结教程
本文我们总结了jQuery操作DOM的事件,及jQuery针对DOM插入操作的方法,这些都是jQuery高级技巧,学习jQuery可以掌握。
页面加载之DOMReady事件:
所谓domReady,也就是文档就绪,我们都知道,在操作dom时必须要在dom树加载完成后才能进行操作。如何检测DOM树已经构建完成,以下是一些实现的方式:
1.使用jQuery:
- // with jQuery
- $(document).ready(function(){ /* ... */ });
- // shorter jQuery version
- $(function(){ /* ... */ });
2.监听DOMContentLoaded事件,DOM 树创建完成后会触发,不支持IE10以下版本。
- // without jQuery (doesn't work in older IEs)
- document.addEventListener('DOMContentLoaded', function(){
- // your code goes here
- }, false);
3.监听readyState状态,可实现跨浏览器
readyState 的状态属性:
- "uninitialized" ? 原始状态
- "loading" ? 下载数据中
- "loaded" ? 下载完成
- "interactive" ? 还未执行完毕
- "complete" ? 脚本执行完毕
- r(function(){
- alert('DOM Ready!');
- });
- function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
这个方法是不断监听readyState的loading状态,加载完成后则执行对应方法。具体可参考:http://www.dustindiaz.com/smallest-domready-ever
根据特定页面的执行对应的代码
如果所有页面的代码都写在一个JavaScript文件中,这样的代码就会难以维护。简单的办法就是根据不同的页面执行不同的代码。来看下例子:
例如在test.js有以下代码:
- var route = {
- _routes: {}, // The routes will be stored here
- add: function(url, callback) {
- this._routes[url] = callback;
- },
- run: function() {
- jQuery.each(this._routes, function(pattern) { // pattern 指向_routes对象集合的key,即url
- if (location.href.match(pattern)) {
- // "this" points to the function to be executed
- this(); //this 指向指向 _routes对象集合的value,即要执行的方法
- }
- });
- }
- }
- // Will execute only on this page:
- route.add('test.html', function() {
- alert('Hello there!');
- });
- route.add('products.html', function() {
- alert("this won't be executed :(");
- });
- // You can even use regex-es:
- route.add('.*.html', function() {
- alert('This is using a regex!');
- });
- route.run();
用逻辑与运算符
利用逻辑与运算符可以简化条件分支语句写法,例子:
一般的写法:
- // Instead of writing this:
- if($('#elem').length){
- // do something
- }
更好的写法:
- $('#elem').length && alert("doing something");
非常有用的jquery is()方法
is()方法非常有用,来看些例子:
HTML:
- <div id="elem"></div>
JS:
- // 变量保存jQuery对象
- var elem = $('#elem');
- // 判断是否为div
- elem.is('div') && console.log("it's a div");
- // 是否包含类名.bigbox
- elem.is('.bigbox') && console.log("it has the bigbox class!");
- // 是否可见
- elem.is(':not(:visible)') && console.log("it is hidden!");
- // 设置元素执行动画
- elem.animate({'width':200},1);
- // 是否执行动画
- elem.is(':animated') && console.log("it is animated!");
定义一个exists函数
判断一个jQuery对象是否存在需要判断length属性,可以封装为exists函数,简化代码,更加易读。
HTML:
- <div id="elem"></div>
JS:
- //一般方法
- console.log($('#elem').length == 1 ? "exists!" : "doesn't exist!");
- // 封装方法
- jQuery.fn.exists = function(){ return this.length > 0; }
- console.log($('#elem').exists() ? "exists!" : "doesn't exist!");
使用$()函数的第二个参数
$()函数可以接收两个参数,第二个参数的作用是什么,可以来看下例子:
- <ul id="firstList" >
- <li>one</li>
- <li>two</li>
- <li>three</li>
- </ul>
- <ul id="secondList" >
- <li>blue</li>
- <li>green</li>
- </ul>
作用一:
- //选取一个元素,通过#firstList限制元素只能在当前的ul节点范围内选取
- $('li' , '#firstList' ). each(function(){
- console.log($(this). html());
- });
- //相当于$('#firstList' ). find('li' );
作用二:
- //创建一个元素。第二个参数为对应的配置属性,包含jQuery方法会被执行
- var div = $('<div>' ,{
- "class" : "bigBlue" ,
- "css" : {
- "background-color" : "purple"
- },
- "width" : 20,
- "height" : 20,
- "animate" : { //使用jQuery的方法作为属性
- "width" : 200,
- "height" : 50
- }
- });
- div. appendTo('body' );
取消右键Click事件
- $(function(){
- $(document).on("contextmenu" , function(e){
- e. preventDefault();
- });
- });
取消文本选中
- //适应于所有浏览器
- $('p.descr' ). attr('un<a href="/tags.php/select/" target="_blank">select</a>able' , 'on' )
- . css('user-select' , 'none' )
- . on('selectstart' , false);
解析锚元素URL
- // 需要解析的URL
- var url = 'http://tutorialzine.com/books/jquery-trickshots?trick=12#comments' ;
- // 通过url创建一个新的链接
- var a = $('<a>' ,{ href: url });
- console. log('Host name: ' + a. prop('hostname' ));
- console. log('Path: ' + a. prop('pathname' ));
- console. log('Query: ' + a. prop('search' ));
- console. log('Protocol: ' + a. prop('protocol' ));
- console. log('Hash: ' + a. prop('hash' ));
输出结果:
- Host name: tutorialzine.com
- Path: /books/jquery-trickshots
- Query: ?trick=12
- Protocol: http:
- Hash: #comments
解密jQuery内核 DOM操作
jQuery针对DOM操作的插入的方法有大概10种
append、prepend、before、after、replaceWith
appendTo、prependTo、insertBefore、insertAfter、replaceAll
分2组,上下对照,实现同样的功能。主要的不同是语法——特别是内容和目标的位置
依赖的domManip,buildFragment模块在之前就分析过了
在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点
对于 .after(), 选择表达式在函数的前面,参数是将要插入的内容。
对于.insertAfter(), 刚好相反,内容在方法前面,它将被放在参数里元素的后面。
after
- after: function() {
- return this.domManip( arguments, function( elem ) {
- if ( this.parentNode ) {
- this.parentNode.insertBefore( elem, this.nextSibling );
- }
- });
- },
之前提过了所有的方法靠this.domManip合并参数处理,内部通过buildFragment模块构建文档碎片
然后把每一个方法的具体执行通过回调的方式提供出来处理
DOM操作并未提供一个直接可以在当前节点后插入一个兄弟节点的方法,但是提供了一个类似的方法
insertBefore() 方法:可在已有的子节点前插入一个新的子节点。语法 :insertBefore(newchild,refchild)
看看jQuery如何处理的
例如
- inner.after('<p>Test</p>');
内部就会把 '
Test
' 通过buildFragment构建出文档elem
然后通过 this.parentNode.insertBefore( elem, this.nextSibling );
这里的this 就是对应着inner ,elem就是‘
Test
’
看到这里就很好理解了after的实现了
用原生方法简单模拟
- var inner = document.getElementsByClassName('inner')
- for(var i =0 ; i<inner.length;i++){
- var elem = inner[i]
- var div = document.createElement('div')
- div.innerHTML = 'aaaa'
- elem.parentNode.insertBefore(div,elem.nextSibling)
- }
insertAfter
jQuery代码的设计者很聪明的,都尽可能的合并相似功能的方法,代码更加精炼美观
- jQuery.each({
- appendTo: "append",
- prependTo: "prepend",
- insertBefore: "before",
- insertAfter: "after",
- replaceAll: "replaceWith"
- }, function( name, original ) {
- jQuery.fn[ name ] = function( selector ) {
- };
- });
DEMO
- $('<p>Test</p>').insertAfter('.inner');
通过$('
Test
')构建一个文档,对象通过insertAfter方法插入到所有class等于inner的节点后
表达的意思与after是一样的,主要的不同是语法——特别是内容和目标的位置
- jQuery.fn[ name ] = function( selector ) {
- var elems,
- ret = [],
- insert = jQuery( selector ),
- last = insert.length - 1,
- i = 0;
- for ( ; i <= last; i++ ) {
- elems = i === last ? this : this.clone( true );
- jQuery( insert[ i ] )[ original ]( elems );
- // Support: QtWebKit
- // .get() because core_push.apply(_, arraylike) throws
- core_push.apply( ret, elems.get() );
- }
- return this.pushStack( ret );
- };
看具体的实现方法中.insertAfter('.inner');inner其实就被当作selector传入进来了
selector可能只是字符串选择器内部就需要转化,insert = jQuery( selector ),
$('
Test
')就是构建出来的文档碎片节点,那么如果赋给insert有多个的时候就需要完全克隆一份副本了,所以就直接赋给
elems = i === last ? this : this.clone( true ); jQuery( insert[ i ] )[ original ]( elems );
依旧是执行after
jQuery( insert[ i ] )[ original ]( elems );
最终还需要返回这个构建的新节点
收集构建的节点
core_push.apply( ret, elems.get() );
构建一个新jQuery对象,以便实现链式
this.pushStack( ret );
可见after 与 insertAfter 本质本质其实都是一样的,只是通过不同的方式调用
before()
根据参数设定,在匹配元素的前面插入内容
- before: function() {
- return this.domManip( arguments, function( elem ) {
- if ( this.parentNode ) {
- this.parentNode.insertBefore( elem, this );
- }
- });
- },
类似after只是替换了第二个参数,改变插入的位置
append()
在每个匹配元素里面的末尾处插入参数内容
- append: function() {
- return this.domManip( arguments, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
- var target = manipulationTarget( this, elem );
- target.appendChild( elem );
- }
- });
- },
内部增加节点,直接可以调用appendChild方法
prepend()
将参数内容插入到每个匹配元素的前面(元素内部)
- prepend: function() {
- return this.domManip( arguments, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
- var target = manipulationTarget( this, elem );
- target.insertBefore( elem, target.firstChild );
- }
- });
- },
类似after只是替换了第二个参数,改变插入的位置
replaceWith()
用提供的内容替换集合中所有匹配的元素并且返回被删除元素的集合。
.replaceWith()可以从DOM中移除内容,然后在这个地方插入新的内容。请看下面的例子:
- <div class="container">
- <div class="inner first">Hello</div>
- <div class="inner second">And</div>
- <div class="inner third">Goodbye</div>
- </div>
我们可以用指定的HTML替换第二个 inner
$('div.second').replaceWith('
New heading
');
结果如下:
- <div class="container">
- <div class="inner first">Hello</div>
- <h2>New heading</h2>
- <div class="inner third">Goodbye</div>
- </div>
或者我们可以选择一个元素把它当做替换的内容:
$('div.third').replaceWith($('.first'));
结果如下:
- <div class="container">
- <div class="inner second">And</div>
- <div class="inner first">Hello</div>
- </div>
从这个例子可以看出,用来替换的元素从老地方移到新位置,而不是复制。
.replaceWith()方法,和大部分其他jQuery方法一样,返回jQuery对象,所以可以和其他方法链接使用,但是需要注意的是:对于该方法而言,该对象指向已经从 DOM 中被移除的对象,而不是指向替换用的对象。
- replaceWith: function() {
- var
- // Sna<a href="/fw/photo.html" target="_blank">ps</a>hot the DOM in case .domManip sweeps something relevant into its fragment
- args = jQuery.map( this, function( elem ) {
- return [ elem.nextSibling, elem.parentNode ];
- }),
- i = 0;
- // Make the changes, replacing each context element with the new content
- this.domManip( arguments, function( elem ) {
- var next = args[ i++ ],
- parent = args[ i++ ];
- if ( parent ) {
- // Don't use the snapshot next if it has moved (#13810)
- if ( next && next.parentNode !== parent ) {
- next = this.nextSibling;
- }
- jQuery( this ).remove();
- parent.insertBefore( elem, next );
- }
- // Allow new content to <a href="/tags.php/include/" target="_blank">include</a> elements from the context set
- }, true );
- // Force removal if there was no new content (e.g., from empty arguments)
- return i ? this : this.remove();
- },
删除目标节点
jQuery( this ).remove();
然后再插入一个新节点
parent.insertBefore( elem, next );