jQuery分析和使用

342 阅读4分钟

jQuery简介

jQuery是一个优秀的javascript代码库(JS库)。顾名思义:j → js,Query → 查询;

所以可知jQuery主要是用来做查询的,即获取文档元素进行操作。

jQuery副标题是:write less,do more.

所以可知使用jQuery可以简化原生DOM编程;

jQuery特点:

  1. 经典:jQuery里包含了很多经典的设计思想,值得学习;
  2. 流行: jQuery是目前前端最长寿的库,也是世界上使用范围最广的库;
  3. 好用:封装了常用的代码,提供简洁的设计模式,上手快,用起来爽;
  4. 兼容:jQuery帮助开发者解决了很多浏览器兼容问题

浅析jQuery设计思想

先上代码:

//  先提供一个全局函数:
window.jQuery = function(){}

// 实现一个简单的API:
// 目标效果:element.addClass('red')能给元素添加类
window.jQuery = function(selector){ 
	// 接受一个选择器,用来获取元素
	const elements = document.querySelectorAll(selector)
    const api = {
    	addClass(className){
        	// 因为获取到的可能不止一个元素,而是多个元素组成的伪数组
            // 所以要遍历
        	for(let i = 0;i < elements.length;i++){
            	elements[i].classList.add(className)
            }
            return api
        }
    }
    return api
}

思路分析:

jQuery是一个函数,接受一个CSS选择器,给它这个选择器,它就能获取到这些元素。但是jQuery不会去返回这些元素,而是返回一些对象。

这些对象里面的内容是一个方法,也就是一个函数,这些函数可以去操作刚刚获取到的这些元素:比如改变元素的样式,修改元素里的文本内容等。

结构示意:

window.jQuery(选择器){
	元素(们) = document.querySelectorAll(选择器)
    对象 = {
    	方法1:函数(){操作获取到的元素},
        方法2:函数(){操作获取到的元素},
        ...
    }
    return 对象
}

上面的代码中,对象里方法访问了外部的元素,也就是访问了外部的变量,所以这里用到了闭包

因为对象里的方法操作了元素,然后返回的还是本来这个对象,所以可以继续调用对象里的方法:

html:
<div id="test">
	<div class="child">1</div>
	<div class="child">2</div>
	<div class="child">3</div>
</div>

jQuery('.child').addClass('red').addClass('blue').addClass('green')

// jQuery获取到所有类名是child的元素,然后用对象的方法来操作元素,操作完又返回对象,又可以接着把方法给"点"出来
// 这就是jQuery的链式风格

// 这样看可能更直观
jQuery('.child').addClass('red')
				.addClass('blue')
				.addClass('green')

// 换个方式理解:
obj.fn(p1) 
// 对象obj调用一个函数fn,p1为函数的接受参数
// 你在调用fn这个函数的时候,肯定是通过obj对象来调用的,类似于上面的addClass,是通过api对象调用的
// 所以这里把前面调用的对象当做this,用call的形式体现出来就是:
obj.fn,call(obj,p1)

// 那么可想而知:如果调用完函数,return这个前面这个对象,那不就可以继续调用对象里的方法了吗?
// 这就相当于把这个对象从前面传到了后面,传到后面后又能接着调用函数,这就是链式操作

结合上面的思考,把代码改写一下:

window.jQuery = function(selector){ 
	const elements = document.querySelectorAll(selector)
    return {
    	addClass(className){
        	for(let i = 0;i < elements.length;i++){
            	elements[i].classList.add(className)
            }
            return this // 函数用对象来调用,那么函数的this就是指向前面这个对象,所以直接改成返回this
        }
    }
}

这里注意不要因为多个return把结构搞混: 第一个 return 是下面的 return api 修改而来的,属于 jQuery 这个函数的返回值; 第二个 return 是对象里的 addClass 方法的返回值;

jQuery是构造函数吗

是:jQuery函数确实构造出来了一个对象;

不是:正常的构造函数去构造一个实例对象需要用到 new 关键字,但是jQuery却没有;

// 比如:
let object = new Object()
Object是个构造函数
对象object是构造函数Object构造出来的对象

let array = new Array()
Array是个构造函数
对象array是构造函数Array构造出来的对象

let function = new Function()
Function是个构造函数
函数对象function是构造函数Function构造出来的对象

总结:

jQuery是构造函数,但不是严格意义上的构造函数。说它是构造函数,是因为它确实构造出了一个对象。说它不是严格意义的上的构造函数,是因为它在构造对象时不用加 new;

$

为了追求更简洁更方便的写法,jQuery作者给jQuery加了一个别名:$

window.jQuery = function(){...}

// 写完jQuery代码后:
window.$ = window.jQuery

// 要放在jQuery后面,而不是前面
// 更简洁的写法:
window.$ = window.jQuery = function(){...}

jQuery命名风格

DOM对象使用DOM的API,比如:querySelector、appendChild...

jQuery对象使用jQuery的API,比如:find、has...

假如遇到这样的代码,就容易让人产生误解:

const div = $('div#test')

乍得一看会以为div是DOM对象,但是实际上div是jQuery构造的对象,如何避免这种情况呢?

为了避免这种情况,所以需要一个约定:

代码中所有jQuery对象,以$命名开头

const div = document.querySelector('.test') // DOM对象
const $div = $('div#test') // jQuery对象

// $div.appendChild不存在,因为它不是DOM对象
// $div.find存在,因为它是jQuery对象

这样可以迅速的确定的一个对象是DOM对象还是jQuery对象,比较方便;

jQuery使用方法

jQuery的基本思想和主要用法,就是选择某个网页元素,然后对其进行操作;

获取网页元素

要获取、选择网页里的元素,需要将一个选择表达式,放进构造函数jQuery( )里,即$( ),这里的表达式可以是CSS选择器:

// 选择整个文档对象
$(document)

// 选择ID为myId的网页元素
$('.myId')

// 选择class为myClass的div元素
$('div.myClass')

// 选择name属性是first的input元素
$('input[name=first]')

上面都是我们熟悉的CSS选择器,另外jQuery还有一些特有的表达式:

// 选择网页中第一个a元素
$('a:first')

// 选择表格的奇数行
$('tr:odd')

// 选择id为myForm的表单里的input元素
$('#myForm:input')

// 选择可见的div元素
$('div:visible')

// 选择所有的div元素,除了前三个
$('div:gt(2)')
// gt:greater than 大于

// 选择当前处于动画状态的div元素
$('div:animated')

改变获取到的结果集

jQuery提供了很多强大的过滤器,可以对获取到的结果进行筛选,缩小选择的范围:

// 选择包含p元素的div元素
$('div').has('p')

// 选择类名class等于myClass的div元素
$('div').filter('.myClass')

// 选择类名class不等于myClass的div元素
$('div').not('myClass')

// 选择第一个div元素
$('div').first()

// 选择第六个div元素
$('div').eq(5)
// eq:equal 等于

从获取到的结果集合开始,移动到附近的相关元素:

// 选择div元素后面的第一个p元素
$('div').next('p')

// 选择div元素的父元素
$('div').parent()

// 选择离div最近的form父元素
$('div').closest('form')

// 选择div元素的所有子元素
$('div').children()

// 选择div元素的同级元素,也就是兄弟姐妹元素
$('div').siblings()

链式操作

原生DOM编程里,获取一个元素,然后对它进行一系列的操作:

// html:
<div id='test'></div>

// js:
let x = document.querySelector('#test');
x.innerText = 'hi';
x.style.background = 'red';
x.style.color = 'blue'

每一次操作都要单独写一行,不够方便。在jQuery里,选中网页元素后,可以对元素进行一系列的操作,并且所有操作都可以连在一起,以链条的形式写出来:

// 用jQuery改写上面的代码:
$('#test').text('hi').css({"background":"red","color":"blue"})

这样就比较简洁方便了,分解链条看看:

$('div').find('h3').eq(2).html('hello')

// 分解:
$('div') // 找到div元素
  .find('h3') // 获取div元素里的h3元素
  .eq(2) // 获取第三个h3元素
  .html('hello') // 把它的html内容改为hello

这就是jQuery的链式操作,每一步的jQuery操作,返回的都是jQuery对象,所以不同的操作可以连接在一起。

jQuery还提供了.end()方法,让结果可以后退一步:

$('div')
  .find('h3')
  .eq(2)
  .html('hello')
  .end() // 退回到选中的所有h3元素那一步
  .eq(0) // 选择第一个h3元素
  .html('World') // 把获取到的第一个h3元素内容改为World

元素操作:取值和赋值

操作网页元素,比较常见的需求就是取得元素的值,或者对它们进行赋值。

jQuery设计思想之四:使用同一个函数,来完成取值和赋值,也就是取值器和赋值器合一,(getter和setter),至于到底是取值还是赋值,由函数的参数来决定:

$('h1').html() // html()括号里没参数,那么就表示取出h1的值
$('h1').html('hi') // html()括号里有参数'hi',那么就表示对h1进行赋值

常见的取值和赋值函数:

// 取出或设置html内容
.html()

// 取出或设置text内容
.text()

// 取出或设置某个属性的值
.attr()

// 取出或设置某个元素的宽度
.width()

// 取出或设置某个元素的高度
.height()

// 取出或设置某个表单元素的值
.val()

注意:如果获取到的结果集合包含多个元素,那么赋值时,会对所有元素进行赋值;取值时,只会取第一个元素值。不过.text()是个特例,它会获取到所有元素的text内容。

元素操作:移动

jQuery设计思想之五:提供两组方法,来操作元素在网页中的位置移动。一组方法是直接移动元素,另一组方法是移动其他元素,达到移动原本想要移动位置的元素的目的。

// 假如我们选中一个div元素,需要把它移动到p元素后面:

// 方法1:用insertAfter()把div元素移动到p元素后面
$('div').insertAfter($('p'))

// 方法2:用after()把p元素加到div元素前面
$('p').after($('div'))

这两种方法表面上看,达到的效果是一样的,唯一的不同只是主体不同,视角不同。

但是实际上两个方法有一个重大的差别,就是返回的元素是不同的:

第一种方法返回的是div元素,第二种方法返回的是p元素,我们可以根据需要来选择相应的方法。

同模式的操作方法:

// 在获取的元素后面插入元素
insertAfter()和after()

// 在获取的元素前面插入元素
insertBefore()和before()

// 在获取的元素内部,从后面插入元素
appendTo()和append()

// 在获取的元素内部,从前面插入元素
prependTo()和prepend()

元素操作:复制、删除和创建

// 复制元素
clone()

// 删除元素
remove() 和 detach
// 两者区别:前者不保留被删除的元素,后者保留,以便重新插入文档来使用

// 清空元素内容,但是不删除元素
empty()

// 创建新元素:把新元素传入jQuery构造函数就行
$('<p>Hi</p>')

$('ul').append($('<li class="new">new list item</li>'))

工具方法

jQuery设计思想之六:除了对选择元素进行操作以外,还提供了一些与元素无关的工具方法(utility),不用选中元素也可以直接使用,就像JS的原生函数一样,可以直接用。

工具方法的实质:其实这里是用到了JS的继承原理,工具方法是jQuery构造函数上的方法,即jQuery.method(),所以可以直接使用。而那些操作元素的方法,则是定义在构造函数的prototype对象上的方法,即jQuery.prototype.method(),所以必须要生成实例才能使用。

常见工具方法:

// 去除字符串两端的空格
$.trim()

// 遍历一个数组或者对象
$.each()

// 返回一个值在数组中的索引位置,如果改值不在这个数组里,就返回-1
$.inArray()

// 返回数组中符合某个标准的元素
$.grep()

// 把多个对象合并成一个对象
$.extend()

// 把对象转换成数组
$.makeArray()

// 判断对象的类别(函数对象、日期对象、数组对象、正则对象等)
$.type

// 判断某个参数是不是数组
$.isArray()

// 判断某个对象是否为空(也就是对象不含有任何属性)
$.isEmptyObject()

// 判断某个对象是否为函数
$.isFunction()

// 判断参数是否为用 { } 或者 new Object 建立的对象
$.isPlainObject()

// 判断浏览器是否支持某个特性
$.support()

事件操作

jQuery设计思想之七:把事件直接绑定在网页元素上;

示例:

$('p').click(function(){
	alert('hi')
});

jQuery支持事件:

// 表单元素获得焦点
.focus()

// 表单元素失去焦点
.blur()

// 表单元素的值发生变化
.change()

// 子元素获得焦点
.focusin()

// 子元素失去焦点
.focusout()

// 鼠标单击
.click()

// 鼠标双击
.dbclick()

// 同时为mouseenter和mouseleave事件指定处理函数
.hover()

// 按下键盘,长时间按键只返回一个事件
.keydown()

// 按下键盘,长时间按键会返回多个事件
.keypress()

// 松开键盘
.keyup()

// 元素加载完毕
.load()

// 按下鼠标
.mousedown()

// 松开鼠标
.mouseup()

// 鼠标进入(进入子元素不会触发)
.mouseenter()

// 鼠标离开(离开子元素不会触发)
.mouseleave()

// 鼠标进入(进入子元素也会触发)
.mouseover()

// 鼠标离开(离开子元素也会触发)
.mouseout()

// 鼠标在元素内部移动
.mousemove()

// DOM加载完成
.ready()

// 浏览器窗口的大小发生改变
.resize()

// 滚动条位置发生变化
.scroll()

// 用户选中文本框的内容
.select()

// 用户提交表单
.submit()

// 根据鼠标点击的次数,依次运行多个函数
.toggle

// 用户离开界面
.unload()

bind

上面的事件都在jQuery内部,都是.bind()的方便的用法,使用.bind()可以更灵活的控制事件,比如多个事件绑定同一个函数:

$('input').bind(
	'click change',//同时绑定click和change事件
    function(){
    	alert('hello');
    }
)

unbind

使用unbind解除事件绑定:

$('p').unbind('click');

one

如果只想让事件运行一次,可以使用.one()方法:

$('p').one('click',function(){
	alert('hi') //这个事件只会运行一次,之后再点击就不会运行了
})

事件对象

所有事件处理函数,都可以接受一个事件对象(event object)作为参数,比如下面的e:

$('p').click(function(e){
	console.log(e.type); // "click"
})

这个事件对象有一些属性和方法:

// 事件发生时,鼠标距离网页左上角的水平距离
event.pageX

// 事件发生时,鼠标距离网页左上角的垂直距离
event.pageY

// 事件的类型(比如:click)
event.type

// 按下了哪个键
event.which

// 在事件对象上绑定数据,然后传入事件处理函数
event.data

// 事件针对的网页元素
event.target

// 阻止事件的默认行为(比如点击链接,会自动打开新页面)
event.preventDefault()

// 停止事件向上层元素冒泡
event.stopPropagation()

在事件处理函数中,可以用this关键字,返回事件针对的DOM元素:

$('a').click(function(e){
	if($(this).attr('href').match('evil')){ // 如果认为是有害链接
    	e.preventDefault(); // 阻止打开
        $(this).addClass('evil'); // 并且加上一个类名,表示链接有害
    }
})

自动触发一个事件:

// 两种方法自动触发事件
// 先定义好事件:
$('a').click(function(){
	alert('clicked!')
})

// 第一种:直接使用事件函数
$('a').click();

// 第二种:用.trigger()或.triggerHandler()
$('a').trigger('click');

特殊效果

jQuery允许对象呈现一些特殊的效果;

// 隐藏一个元素
.hide()

// 显示元素
.show()

// 淡入
.fadeIn()

// 淡出
.fadeOut()

// 调整透明度
.fadeTo()

// 向下展开
.slideFDown()

// 向上卷起
.slideUp()

// 依次展开或卷起某个元素
.slideToggle()

// 依次展示或隐藏某个元素
.toggle()

注意:除了.show()和.hide(),其他所有特效的默认执行时间都是400毫秒(ms),我们可以手动改变这个时间:

$('p').fadeIn(300) // 300毫秒内淡入
$('p').fadeOut('slow') // 缓慢的淡出 

特效结束以后,还可以指定执行一个函数:

$('p').fadeIn(300,function(){
	$(this).remove();
}) 

想要一些更复杂的特效,可以自己用.animate()来定义:

$('div').animate({
	{
    	left:'+=50', // 不断往右移
        opacity: 0.25 // 指定透明度
    },
    300// 执行时间
    function(){
    	alert('done!') // 回调函数
    }
})

可以用.stop()和.delay()来停止或延缓特效的执行;

关闭所有网页特效:把$.fx.off的值设置为true即可;

推荐博客:jQuery设计思想-阮一峰