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文件即可

<html>
<head>
    <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
    ...
</head>
<body>
    ...
</body>
</html>

$符号

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

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

jQuery入口函数

//方法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’)来选择,层级之间用空格隔开。例如:

<!-- 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’)类似层级选择器,但是限定了层级关系必须是父子关系,就是节点必须是节点的直属子节点。还是以上面的例子:

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

过滤器

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

$('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结构:

<!-- 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()查找子节点

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()查找父节点

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

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

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()方法可以过滤掉不符合选择器条件的节点:

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

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

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节点转化为其他对象:

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节点去掉:

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()方法,例如:

<div id="test-div">
    <ul>
        <li><span>JavaScript</span></li>
        <li><span>Python</span></li>
        <li><span>Swift</span></li>
    </ul>
</div>

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

    节点:

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

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

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

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

    // 创建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节点:

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

    detach方法

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

    empty方法

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

    复制节点

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

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

    替换节点

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

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

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

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

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

    包裹节点

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

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

    <strong>有线网络</strong>
    <strong>无线网络</strong>
    <ul>
    	<li>GY</li>
    	<li>NetChina</li>
    </ul>
    

    如果使用wrap()

    $("strong").wrap("<b></b>");
    
    //结果
    //<b><strong>有线网络</strong></b>
    //<b><strong>无线网络</strong></b>
    

    如果使用wrapAll()方法

    $("strong").wrapAll("<b></b>");
    
    //结果
    //<b>
    //	<strong>有线网络</strong>
    //	<strong>无线网络</strong>
    //</b>
    

    如果用wrapInner()方法

    $("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

        <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 部分

    *{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实现

    $(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.

    三种变体写法

    第一种:

    $(".wrap li").hover(function(){
        $(this).children("ul").show();
    }, function(){
        $(this).children("ul").hide();
    })
    

    第二种:

    $(".wrap li").hover(function(){
        var $this = $(this).children("ul");
        var isShow = $this.css("display");
        if(isShow === "block"){
            $this.hide();
        }else{
            $this.show();
        }
    });
    

    第三种:

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

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

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