Web前端基础(15)jQuery选择器和DOM操作

jQuery是JavaScript中使用最广泛的一个库,它可以帮助我们简化js代码,更好的完成工作。

痛点

javascript遇到的一些痛点:

  • window.onload 事件有个覆盖问题,我们只能写一个
  • 代码容错性差
  • 浏览器兼容性问题
  • 书写很繁琐,代码量多
  • 代码很乱,各个页面到处都是
  • 动画效果,我们很难实现

版本

目前jQuery有三个大版本:

  • 1.x:兼容ie678,使用最为广泛的,官方只做BUG维护,功能不再新增。因此一般项目来说,使用1.x版本就可以了,最终版本:1.12.4 (2016年5月20日)

  • 2.x:不兼容ie678,很少有人使用,官方只做BUG维护,功能不再新增。如果不考虑兼容低版本的浏览器可以使用2.x,最终版本:2.2.4 (2016年5月20日)

  • 3.x:不兼容ie678,只支持最新的浏览器。除非特殊要求,一般不会使用3.x版本的,很多老的jQuery插件不支持这个版本。目前该版本是官方主要更新维护的版本。最新版本:3.3.1

引入

使用jQuery只需要在页面的

引入jQuery文件即可

1
2
3
4
5
6
7
8
9
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
...
</head>
<body>
...
</body>
</html>

$符号

$是著名的jQuery符号。实际上,jQuery把所有功能全部封装在一个全局变量jQuery中,而$也是一个合法的变量名,它是变量jQuery的别名:

1
2
3
4
window.jQuery; // jQuery(selector, context)
window.$; // jQuery(selector, context)
$ === jQuery; // true
typeof($); // 'function'

jQuery入口函数

1
2
3
4
//方法1:
$(document).ready(function(){});
//方法2:
$(function(){});

javascript的入口函数和jquery的入口函数区别:

  1. javascript的window.onload事件是等到所有内容,以及我们外部图片之类的文件加载完了之后,才会去执行。jQuery入口函数是在html所有标签都加载进去后执行。
  2. jQuery入口函数不会有被覆盖问题。

选择器

jQuery的基本选择器

符号说明
$(“#demo”)选择id为dem的第一个元素
$(“.item”)选择所有class=”item”的元素
$(“div”)选择所有div标签
$(“*”)选择所有元素(建议配合其他选择器使用)
$(“.item, div”)组合选择器(选择class=”item”和div选择器)
$(“div span)交集选择器(选择div标签下的所有span标签)
$(“div > span”)子元素选择器(选择div的子标签中的span标签(只是直接子标签))
$(“div + span”)相邻元素选择器(选择div相邻的span)

层级选择器

如果两个DOM元素具有层级关系,就可以用$(‘ancestor descendant’)来选择,层级之间用空格隔开。例如:

1
2
3
4
5
6
7
8
<!-- HTML结构 -->
<div class="testing">
<ul class="lang">
<li class="lang-javascript">JavaScript</li>
<li class="lang-python">Python</li>
<li class="lang-lua">Lua</li>
</ul>
</div>

子选择器

子选择器$(‘parent>child’)类似层级选择器,但是限定了层级关系必须是父子关系,就是节点必须是节点的直属子节点。还是以上面的例子:

1
2
$('ul.lang>li.lang-javascript'); // 可以选出[<li class="lang-javascript">JavaScript</li>]
$('div.testing>li.lang-javascript'); // [], 无法选出,因为<div>和<li>不构成父子关系

过滤器

过滤器一般不单独使用,它通常附加在选择器上,帮助我们更精确地定位元素。观察过滤器的效果:

1
2
3
4
5
6
7
$('ul.lang li'); // 选出JavaScript、Python和Lua 3个节点

$('ul.lang li:first-child'); // 仅选出JavaScript
$('ul.lang li:last-child'); // 仅选出Lua
$('ul.lang li:nth-child(2)'); // 选出第N个元素,N从1开始
$('ul.lang li:nth-child(even)'); // 选出序号为偶数的元素
$('ul.lang li:nth-child(odd)'); // 选出序号为奇数的元素

jQuery中的基本过滤选择器

符号说明
:eq(index)index是从0开始,表示第index+1个匹配的元素
:gt(index)选择序号大于index的元素
:lt(index)选择序号小于index的元素
:odd序号为奇数的
:even序号为偶数的
:first选择第一个匹配的元素
:last选择最后一个匹配的元素

属性选择器

jQuery中常用的属性选择器

符号说明
$(“a[href]”)选择所有包含href属性的元素
$(“a[href=’label’]”)选择属性为href=”label”的元素
$(“a[href!=’label’]”)选择属性不是href=”label”的元素
$(“a[href^=’label’]”)选择所有href以label开头的元素
$(“a[href$=’label’]”)选择所有href以label结尾的元素
$(“a[href*=’label’]”)选择所有href中包含label的元素
$(“a[href][title=’我’]”)选择所有包含href属性并且title=”我”的元素

查找

通常情况下选择器可以直接定位到我们想要的元素,但是,当我们拿到一个jQuery对象后,还可以以这个对象为基准,进行查找和过滤。

最常见的查找是在某个节点的所有子节点中查找,使用find()方法,它本身又接收一个任意的选择器。例如如下的HTML结构:

1
2
3
4
5
6
7
8
<!-- HTML结构 -->
<ul class="lang">
<li class="js dy">JavaScript</li>
<li class="dy">Python</li>
<li id="swift">Swift</li>
<li class="dy">Scheme</li>
<li name="haskell">Haskell</li>
</ul>

用find()查找子节点

1
2
3
4
var ul = $('ul.lang'); // 获得<ul>
var dy = ul.find('.dy'); // 获得JavaScript, Python, Scheme
var swf = ul.find('#swift'); // 获得Swift
var hsk = ul.find('[name=haskell]'); // 获得Haskell

用parent()查找父节点

1
2
3
var swf = $('#swift'); // 获得Swift
var parent = swf.parent(); // 获得Swift的上层节点<ul>
var a = swf.parent('.red'); // 获得Swift的上层节点<ul>,同时传入过滤条件。如果ul不符合条件,返回空jQuery对象

用next()和prev()查找同层级

1
2
3
4
5
6
7
var swift = $('#swift');

swift.next(); // Scheme
swift.next('[name=haskell]'); // 空的jQuery对象,因为Swift的下一个元素Scheme不符合条件[name=haskell]

swift.prev(); // Python
swift.prev('.dy'); // Python,因为Python同时符合过滤器条件.dy

过滤

和函数式编程的map、filter类似,jQuery对象也有类似的方法。

filter()方法可以过滤掉不符合选择器条件的节点:

1
2
var langs = $('ul.lang li'); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var a = langs.filter('.dy'); // 拿到JavaScript, Python, Scheme

或者传入一个函数,要特别注意函数内部的this被绑定为DOM对象,不是jQuery对象:

1
2
3
4
var langs = $('ul.lang li'); // 拿到JavaScript, Python, Swift, Scheme和Haskell
langs.filter(function () {
return this.innerHTML.indexOf('S') === 0; // 返回S开头的节点
}); // 拿到Swift, Scheme

map()方法把一个jQuery对象包含的若干DOM节点转化为其他对象:

1
2
3
4
var langs = $('ul.lang li'); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var arr = langs.map(function () {
return this.innerHTML;
}).get(); // 用get()拿到包含string的Array:['JavaScript', 'Python', 'Swift', 'Scheme', 'Haskell']

此外,一个jQuery对象如果包含了不止一个DOM节点,first()、last()和slice()方法可以返回一个新的jQuery对象,把不需要的DOM节点去掉:

1
2
3
4
var langs = $('ul.lang li'); // 拿到JavaScript, Python, Swift, Scheme和Haskell
var js = langs.first(); // JavaScript,相当于$('ul.lang li:first-child')
var haskell = langs.last(); // Haskell, 相当于$('ul.lang li:last-child')
var sub = langs.slice(2, 4); // Swift, Scheme, 参数和数组的slice()方法一致

DOM操作

直接使用浏览器提供的API对DOM结构进行修改,不但代码复杂,而且要针对浏览器写不同的代码。

有了jQuery,我们就专注于操作jQuery对象本身,底层的DOM操作由jQuery完成就可以了,这样一来,修改DOM也大大简化了。

添加DOM

要添加新的DOM节点,除了通过jQuery的html()这种暴力方法外,还可以用append()方法,例如:

1
2
3
4
5
6
7
<div id="test-div">
<ul>
<li><span>JavaScript</span></li>
<li><span>Python</span></li>
<li><span>Swift</span></li>
</ul>
</div>

如何向列表新增一个语言?首先要拿到

    节点:

1
var ul = $('#test-div>ul');

然后,调用append()传入HTML片段:

1
ul.append('<li><span>Haskell</span></li>');

除了接受字符串,append()还可以传入原始的DOM对象,jQuery对象和函数对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 创建DOM对象:
var ps = document.createElement('li');
ps.innerHTML = '<span>Pascal</span>';
// 添加DOM对象:
ul.append(ps);

// 添加jQuery对象:
ul.append($('#scheme'));

// 添加函数对象:
ul.append(function (index, html) {
return '<li><span>Language - ' + index + '</span></li>';
});

append()把DOM添加到最后,prepend()则把DOM添加到最前。

另外注意,如果要添加的DOM节点已经存在于HTML文档中,它会首先从文档移除,然后再添加,也就是说,用append(),你可以移动一个DOM节点。

同层级之间可以用用after()或者before()方法。

删除节点

remove方法

要删除DOM节点,拿到jQuery对象后直接调用remove()方法就可以了。如果jQuery对象包含若干DOM节点,实际上可以一次删除多个DOM节点:

1
2
var li = $('#test-div>ul>li');
li.remove(); // 所有<li>全被删除

detach方法

detach()和remove()一样,是从DOM中去掉所有匹配元素,但是不会把jQuery对象中删除,所有关于该节点的事件、附加的数据都会保留下来。

empty方法

empty方法不是删除节点,而是清空元素中的所有后代节点。

复制节点

可用通过clone()方法来复制节点,clone()方法可以传一个boolean参数,如果传true表示复制元素的同时也复制元素中所绑定的事件。

1
$(this).clone(true).appendTo("body");

替换节点

jQuery提供了replaceWith()和replaceAll()方法来替换节点

replaceWith()方法作用是将所有匹配的元素都替换成制定的html或者DOM元素。

1
$("p").replaceWith("<strong>我会把你这个节点替换掉</strong>")

replaceAll()和replaceWith()的作用相同,知识颠倒了操作。

1
$("<strong>我会把里面的所有你都替换掉</strong>").replaceAll("p");

包裹节点

1
$("strong").wrap("<b></b>") //用b标签把<strong>元素包裹起来

包裹元素还有wrapAll()和wrapInner()方法, 与wrap()的区别如下:

1
2
3
4
5
6
<strong>有线网络</strong>
<strong>无线网络</strong>
<ul>
<li>GY</li>
<li>NetChina</li>
</ul>

如果使用wrap()

1
2
3
4
5
$("strong").wrap("<b></b>");

//结果
//<b><strong>有线网络</strong></b>
//<b><strong>无线网络</strong></b>

如果使用wrapAll()方法

1
2
3
4
5
6
7
$("strong").wrapAll("<b></b>");

//结果
//<b>
// <strong>有线网络</strong>
// <strong>无线网络</strong>
//</b>

如果用wrapInner()方法

1
2
3
4
5
$("strong").wrapInner("<b></b>");

//结果
//<strong><b>有线网络</b></strong>
//<strong><b>无线网络</b></strong>

插入节点

方法描述
append()匹配元素内部追加(父子节点)
appendTo()和append()颠倒A.appendTo(B) == B.append(A)
prepend()匹配元素内部最前面插入
prependTo()A.prependTo(B) == B.prepend(A)
after()在匹配元素之后插入(兄弟节点)
insertAfter()A.inertAfter(B) == B.after(A)
before()在匹配元素之前插入(兄弟节点)
insertBefore()A.intertBefore(B) == B.before(A)

DOM对象和jQuery对象切换

在jQuery对象中无法使用DOM对象的任何方法,可以用jQuery类似的方法代替,DOM对象也不能使用jQuery的方法。

$(dom对象) ==> jQuery对象

$(“#btn”)[0] 或者 $(“#btn”).get(0) ==> DOM对象

下拉菜单Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="wrap">
<ul>
<li><a href="#">一级菜单1</a>
<ul>
<li><a href="">二级菜单1</a></li>
<li><a href="">二级菜单2</a></li>
<li><a href="">二级菜单3</a></li>
</ul>
</li>
<li><a href="#">一级菜单2</a>
<ul>
<li><a href="">二级菜单1</a></li>
<li><a href="">二级菜单2</a></li>
<li><a href="">二级菜单3</a></li>
</ul>
</li>
<li><a href="#">一级菜单3</a>
<ul>
<li><a href="">二级菜单1</a></li>
<li><a href="">二级菜单2</a></li>
<li><a href="">二级菜单3</a></li>
</ul>
</li>
</ul>
</div>

css 部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
*{padding: 0; margin: 0;}

ul{
list-style: none;
}

.wrap{
width: 331px;
height: 30px;
margin: 100px auto 0;
background: #888;
padding-left: 10px;
border-radius: 4px;
}

.wrap li{
float: left;
width: 100px;
height: 30px;
margin-right: 10px;
position: relative;
}

.wrap a{
color: black;
text-decoration:none;
display:block;
width: 100px;
height: 28px;
text-align: center;
line-height: 28px;
background: #FFCF00;
border-radius: 6px;
border:1px solid #FF8626;
color: #666;
}

.wrap li ul{
position: absolute;
display: none;
}

js实现

1
2
3
4
5
6
7
8
9
$(document).ready(function(){
$(".wrap li").mouseenter(function(){
$(this).children("ul").show();
});

$(".wrap li").mouseleave(function(){
$(this).children("ul").hide();
});
});

mouseover/mouseout和mouseenter/mouseleave区别

mouseover其中的所有元素进入都会事件触发一次, mouseenter只会触发一次,所以上面使用的是mouseenter.

三种变体写法

第一种:

1
2
3
4
5
$(".wrap li").hover(function(){
$(this).children("ul").show();
}, function(){
$(this).children("ul").hide();
})

第二种:

1
2
3
4
5
6
7
8
9
$(".wrap li").hover(function(){
var $this = $(this).children("ul");
var isShow = $this.css("display");
if(isShow === "block"){
$this.hide();
}else{
$this.show();
}
});

第三种:

1
2
3
$(".wrap li").hover(new function(){
$(this).children("ul").slideToggle();
});

slideToggle()方法是在显示和隐藏之间切换的动画。

本文部分内容参考自廖雪峰的官方网站