jQuery

228 阅读9分钟

jQuery:JavaScript library —— John Resig 2006-8-26

一般公司对交互性要求没那么高的时候,PC用1.x版本,移动端用2.x版本

jQuery cdn cdn.bootcdn.net/ajax/libs/j…

压缩工具:雅虎YUI Compressor、Google JavaScript Closure Compiler

js压缩除了会把换行空格注释去掉,还会修改变量名,缩短变量名以减小文件大小

混淆会修改变量名和函数名为毫无意义的命名,防窃取

jQuery === $

基础语法

选择器

选择器写法和css基本一致

选择器说明
.class/#id/tag
.wrapper ul父子选择器
li: first
li: odd/even奇数/偶数,从0开始
li: eq(num)选择第几个,从0开始,-1从最后一个开始,num写999不会执行也不会报错,因为jQuery有容错机制
li[data='first']选属性名为data值为first的属性
li[data$='th']选择值以th结尾的data属性
li[data^='f']选择值以f开头的data属性
li[data!='f']选择值不是f的data属性,包含f不算

获取元素有两种方法

var one = $('ul li');
var two = $('li', 'ul'); //两个参数,第二个参数是第一个参数的上下文

容错机制:(null)(null),(undefined)……不会报错,不会阻止程序运行,这样做其实是非常好的

例:

$('li').each(function(index, elem){
    $(elem).find('.icon').css({fontSize: '12px', color: 'red'}};
})

这里循环找li里的icon,如果不是每一个li都有icon,也是不会报错的,找不到就算了(容错机制),在原生JS中有一个找不到就会报错

简单重写源码

要实现的目标:

  1. $() JQ对象
  2. jQuery.fn.init 用构造函数实现
  3. 封闭作用域
;(function() {
	function jQuery(selector) {
	    //通过这个函数调用,传入参数,执行init函数,由于init是构造函数,再new实例化一下
		return new jQuery.prototype.init(selector);
	}

	jQuery.prototype.init = function(selector) {
		// this = {}; init函数实例化出来this
		// 按照相应的选择器,找到对应的DOM元素,包装成jQuery对象并返回

		this.length = 0;

		if (selector.indexOf('.') != -1) {
			var elem = document.getElementsByClassName(selector.slice(1));//slice去点
		} else if(selector.indexOf('#') != -1) {
			var elem = document.getElementById(selector.slice(1));
		}

		// 只选出来一个元素的情况下,elem是不会有长度的,没有长度执行下面的循环就会报错
		if (elem.length === undefined) {
			this[0] = elem;
			this.length ++;
		}

		for (var i = 0; i < elem.length; i++) {
			this[i] = elem[i];
			this.length ++;
		}
	}

	// css方法不在jQuery.prototype.init里面,无法调用
	jQuery.prototype.init.prototype = jQuery.prototype;

	jQuery.prototype.css = function(option) {
		for (var i = 0; i < this.length; i++) {
			// this就是init实例化出来的this,里面包含选出来的元素
			for(var attr in option) {
				this[i].style[attr] = option[attr];
			}
		}
		return this; //实现链式调用
	}

	window.$ = window.jQuery = jQuery;
})();

$('#logo').css({width: '300px', height: '300px'})
		  .css({opacity: .2});

没有将元素添加到this对象的话,打印出来的仅仅是原生选出来的类数组

添加了之后

$(document).ready

DOM元素加载完成

$(document).on("ready", function() {...})
$(document).ready(function() {...})
$(function() {...})

window.onload不仅是DOM元素加载完成,还会等img加载完成,对应jQuery:

$(window).on("load", function() {...})
$(window).load(function() {...})

DOM元素加载完成就可以绑定事件了,但是在一些场景下还是要用window.onload,比如说轮播图需要用到图片的宽高,图片没加载玩没法做

选择查找

get

$()获取的是jQuery对象,是类数组;get方法获取到的是原生的js对象

$('li').get()

不传参数、传null、undefined返回的是所有的li元素,传参就是第几个(从0开始)

jQuery.prototype.get = function(number) {
	// if (number == null) {
	// 	return [].slice.call(this, 0);
	// } else {
	// 	if (number >= 0) {
	// 		return this[number];
	// 	} else {
	// 		// 负数就数字+长度
	// 		return this[number + this.length];
	// 	}
	// }
	return number != null
				  ? (number >= 0 ? this[number] : this[number + this.length])
				  : [].slice.call(this, 0);
}

eq

和get最大的区别就是eq返回的是jQuery对象(类数组)

为什么有了eq选择器还有eq方法呢?

因为eq选择器没有办法做到先统一给所有类添加方法,再单独添加方法

$('.demo').css({'color': 'red'}).eq(3).css({'color': 'blue'});
$('.demo:eq(3)').css({'color': 'red'}).css({'color': 'blue'});

没传参返回空,其他用法和get一样

修改源码

jQuery.prototype.init = function(selector) {
	this.length = 0;

	if (selector == null) {
		return this;
	}

	if (typeof selector == 'string' && selector.indexOf('.') != -1) {
		var elem = document.getElementsByClassName(selector.slice(1));
	} else if(typeof selector == 'string' && selector.indexOf('#') != -1) {
		var elem = document.getElementById(selector.slice(1));
	}

	// 原生DOM对象,eq只会选一个
	// if (selector instanceof Element) {
	// 	this[0] = selector;
	// 	this.length++;
	// }

	// 只选出来一个元素的情况下,elem是不会有长度的,没有长度执行下面的循环就会报错
	// if (elem.length === undefined) {
	// 	this[0] = elem;
	// 	this.length ++;
	// }
	if (selector instanceof Element || elem.length === undefined) {
		this[0] = elem || selector;
		this.length ++;
	}

	for (var i = 0; i < elem.length; i++) {
		this[i] = elem[i];
		this.length ++;
	}
}
jQuery.prototype.eq = function(number) {
	var elem =  number != null
					   ? (number >= 0 ? this[number] : this[number + this.length])
					   : [].slice.call(this, 0);
	return jQuery(elem); //数组变成类数组
}

find

在原有的基础上进行查找子元素

$('ul').css({'color': 'blue'});
       .find('li').css({'color': 'yellow'});

prevObject可以查看通过方法找到的元素的上一级,比如li的prevObject是ul,ul(顶级)的prevObject是document。如果在HTML里ul还有上一级的话是不会认的。

jQuery是建立在原生JS的基础上的,最开始就是document.get……

过滤筛选

filter

<div class="container">
	<ul>
		<li>1</li>
		<li class="special-item">2</li>
		<li class="special-item">34</li>
	</ul>
</div>

找到container下面的special-item

$('.container ul').find('li').filter('.special-item');

要注意的是不能在ul上filter,要先选出来一个集合,再filter

和直接用find找到special-item的区别是prevObj不一样

filter还可以传特殊的选择器和函数

$('.container ul').find('li').filter(':even')
$('.container ul').find('li').filter(function(idx, elem) {
    console.log(idx, elem); //this指向elem
    return idx % 2 == 0;
})

not 和filter相反

和filter结果相反,filter返回的是满足条件的元素,not是返回不满足条件的元素

源码涉及到Sizzle.js —— jQuery选择器引擎

has 拥有某个子元素的元素

方便查找元素

<ul>
	<li>
		<ul>
			<li></li>
			<li></li>
			<li></li>
		</ul>
	</li>
	<li></li>
	<li></li>
</ul>
$('li').has('ul');

查找有ul的li元素

is 返回布尔值

判断选出来的元素是否某个标签

<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul>
$('ul').click(function(e) {
    conaole.log(e.target); //原生DOM对象,要使用的话需要转换
    if($(e.target).is('li')){
        console.log($(e.target).text());
    } else {
        console.log($(this).text());
    }
})

add 集中操作

$('ul').css({'color': 'green'})
       .find('li').css({'color': 'green'})

如果不用add的话,想要给li继续地集体添加样式就需要通过find找到所有的li

$('ul').add('li').css({'color': 'green'})

等效第一种写法,ul和li都会加上相同的样式

可以看到第0项多出来父元素li,所以这个方法会新创建一个jQuery实例对象

如果HTML的其他部分也有一样的结构,也会选上

<div id="logo">
	<ul>
		<li></li>
		<li></li>
		<li></li>
	</ul>
</div>
<ul>
	<li></li>
	<li></li>
	<li></li>
</ul>
console.log($('#logo ul').add('li').css({'border': '1px solid black'}));

是个li都会被加上

jQuery.prototype.add = function(selector) {
	var curObj = jQuery(selector),
		baseObj = this,
		newObj = jQuery(); //空对象

	for (var i = 0; i < curObj.length; i++) {
		newObj[newObj.length++] = curObj[i];
	}

	for (var i = 0; i < curObj.length; i++) {
		newObj[newObj.length++] = baseObj[i];
	}
	this.pushStack(newObj);
	return newObj;
}

end 回退

jQuery操作

$('ul').css({...})
$('ul').find('li').css({...})

这两个css操作基于的对象是不一样的。如果你用了li之后又想回到ul,就可以使用end

$('ul').css({'border': '1px solid black'})
       .find('li').css({'border': '1px solid red'})
       .end().css({'border': '1px solid green'});

回退之后返回的新的jQuery对象就是prevObject里的内容

源码实现:

end是靠prevObject实现回退的,很多函数都需要有这个属性,所以抽象一个函数

jQuery.prototype.pushStack = function(elem) {
	if (elem.constructor != jQuery) {
		elem = jQuery(elem);
	}
	elem.prevObject = this;
	return elem;
}
jQuery.prototype.end = function() {
	return this.prevObject;
}

获取内容

html

html相当于innerHTML

<div id="logo">
	<ul id="list">
		<li class="item">1</li>
		<li class="item">2</li>
		<li class="item">3</li>
	</ul>
</div>
$('div').html()

如果是一个集合使用这个方法,只会读取第一个元素的html(特例)

$('div ul li').html()  //1

和css方法不同,css方法会循环操作这个集合

但是赋值操作是可以替换集合里的所有元素的,要特别注意取值和赋值得到的元素不同

$('div ul li').html('<span>替换后的内容</span>')

还可以操作数组

var arr = ['Jackson', 'Daniel', 'Eden'];
$('div ul li').html(function(idx, elem) {
	return '<span>' + arr[idx] + '</span>';
})

text

只会打印文本内容,和innerText一样,一个集合取值会获得所有元素的文本,和html不一样

同样可以赋值和操作数组

size() 相当于length

操作class

addClass

$('div ul li').eq(1).addClass('active warning');

空格jQuery会自动帮你加,不需要在active前面空格,当然你添加多个类名,之间还是要有空格的

$('div ul li').addClass('active warning');

集合添加类名是会循环操作的,也就是说所有li都会添加类名

还可以操作方法

$('div ul li').addClass(function(idx, elem) {
    return 'item-' + idx;
})

并且可以灵活的进行条件判断

$('div ul li').addClass(function(idx, elem) {
    if ((idx + 1) % 2 != 0) {
    	return 'active';
    }
})

removeClass

操作和addClass相同,可以循环删除集合上的类名和传方法

jQuery有非常高的容错机制,如果你删除一个不存在的类名或者传入空串,什么都不会发生;如果删除的多个类名排列顺序和html里的不一致,也没有关系,还是会删除;如果你什么都不传,就会把类名全删了。

$('div ul li').click(function(e) {
	$('.item').removeClass('active');
	$(this).addClass('active');
});

最好不要用css操作,而是通过类添加,原因如下:

  1. 维护
  2. 复用性
  3. css文件是可以缓存的

hasClass

即使你一个元素有多个类名,只要包含要找的类名,也会返回true

css

赋值

$('div').css('color', 'blue');
$('ul').css({'backgroundColor': 'green', 'border': '1px solid yellow'})
       .css('width', '+=100px');

可以在原有的基础上+=

取值

$('div').css('color');

取值取颜色的话,返回rgb

属性相关

attr

取值

$('div').attr('class');
$('input').attr('checked'); //checked

获取checked的时候,不管简写或者不简写都是checked,没有这个属性就是undefined,这个方法其实不太理想,最好是返回true/false,所以有了prop方法

赋值

$('div').attr('class', 'test');

prop

取值和赋值操作和attr一样

$('div').prop('class');
$('input').prop('checked'); //true

总结:

  1. 自定义属性只能通过attr取值和赋值(data-field)
  2. 类似于checked、selected、disabled,用prop方法比较明显

表单元素值取值和赋值

val 单个表单控件值

$('input').eq(0).val("新属性值")

更改属性值之后控制台elem显示的value值没有更改,这只是jQuery没有处理这个部分,实际上打印值还是有更改的,对后续操作没有任何影响

传函数,使用场景不是很多

$('input').eq(0).val(function(idx, oldVal) {
    return oldValue + '新属性值';
})

serialize 表单内所有控件的值

控件要写在表单里才可以获取

$('form').serialize();

返回值是字符串(query string)

"text1=abc&text2=ddd&text3=ejl"

想要返回分开的对象数组

$('form').serializeArray();

each 遍历

ul > li * 5
$("li").each(function(idx, elem) {
    $(elem).text(idx + 1)
        .addClass("test-" + idx);
})

elem是原生DOM对象,如果不用$包的话,可以使用原生JS方法

遍历,将test和addClass两个function合并在一起,提升效率

$("li").text(function() {...})
$("li").addClass(function() {...})

index

$("ul").on("click", "li", function(e) {
    console.log($(e.target).index());
})
$("li").on("click", function(e) {
    console.log($("li").index($(e.target)));
})

两种写法都可以

DOM增删改查

next/prev 上一个/下一个元素

方法内可以传递参数,指定筛选条件

$("button").click(function() {
    $(this).prev("span").css("fontSize", "30px");
})

前一个元素必须是span才可以生效

执行完prev会返回一个新的jQuery对象

nextAll

选择之后所有的元素

应用场景:选择都喜欢之后全部勾选

$("input").eq(0).click(function() {
    if($("input").eq(0).prop("checked")) {
        $(this).nextAll().prop("checked", true);
    } else {
        $(this).nextAll().prop("checked", false);
    }
})

同样可以限制条件

 $(this).nextAll("input[type='checkbox']")

nextUntil

<div id="logo">
	<ul id="list">
		<li class="item">1</li>
		<li class="item">2</li>
		<li class="item">3</li>
	</ul>
</div>
<div>
	<h1>篮球明星</h1>
	都喜欢<input type="checkbox" />
	库里<input type="checkbox" />
	詹姆斯<input type="checkbox" />
	哈登<input type="checkbox" />

	<h1>足球明星</h1>
	都喜欢<input type="checkbox" />
	C罗<input type="checkbox" />
	梅西<input type="checkbox" />
	内马尔<input type="checkbox" />
</div>

如果用nextAll勾选都喜欢的话,所有的选框都会被勾选,要想区分两个部分的选框,就需要nextUntil

$("h1").next().click(function() {
    if($(this).prop("checked")) {
        $(this).nextUntil("h1").prop("checked", true);
    } else {
        $(this).nextUntil("h1").prop("checked", false);
    }
})

siblings 兄弟元素

同级元素,同样可以传入参数筛选

parent 上一级父元素

parents 所有的父级元素 []

children 所有子元素

传入参数(选择器)可以指定需要哪个父级元素

closest 最近的元素,可以选自己

offsetParent 最近的祖先的定位元素

slice(start,end)选择一定范围的元素,左闭右开


增、改

$() 选择、创建元素

$("<div style='color: red'>test</div>").appendTo($("body"))

insertBefore/before;insertAfter/after

$(".box-3").insertBefore(".box-1");

两个掉个顺序,就是另外一个方法(等效)

$(".box-1").before($(".box-3"));

会有两个方法的原因就是链式调用,两个方法在前面的主体不一样

before如果传入的不是jQuery对象,就是一个文本插到前面,insertBefore不用传jQuery对象,只要传选择器就好

appendTo/prependTo 子元素添加到父元素里

append/prepend 父添加子

append是追加,prepend是加到最前面

wrap 被包裹

$("h1").wrap("<div class='container></div>")
$("h1").wrap($("<div class='container></div>"))
$("h1").wrap($(".container")); //复制

将h1通过wrap里的内容(div)包裹起来

这三种方法都可以,但是最后一种方法有个缺陷就是,"container"选择的元素是会复制过来的,而不是剪切,也就是说原来的"container"元素还在

函数

$("h1").wrap(function(idx) {
    return "<div class='container-" + idx + "'></div>"
})

wrapInner 内部添加一层

<div class="container">
	<h1>text</h1>
	<h1>text</h1>
</div>
$(".container").wrapInner("<div></div>")

结果

<div class="container">
    <div>
        <h1>text</h1>
    	<h1>text</h1>
    </div>
</div>

传函数也可

wrapAll 包裹有统一类名的元素到

<div class="container">
	<h1>text</h1>
	<h1>text</h1>
</div>
<div class="container">
	<h1>text</h1>
	<h1>text</h1>
</div>
$(".container").wrapAll("<div class='wrap'></div>")

result:

<div class='wrap'>
    <div class="container">
    	<h1>text</h1>
    	<h1>text</h1>
    </div>
    <div class="container">
    	<h1>text</h1>
    	<h1>text</h1>
    </div>
</div>

unwrap 去掉外层

<div class='wrap'>
    <div class="container">
    	<h1>text</h1>
    	<h1>text</h1>
    </div>
</div>
$("h1").unwrap()

result:

<div class='wrap'>
	<h1>text</h1>
    <h1>text</h1>
</div>

可以连续调用,一直去外层,到wrap为止

clone

默认不克隆事件,要克隆事件就传参true

自定义属性克隆不过去,用data

例:模板复制

<table>
	<tr>
		<th>姓名</th>
		<th>年龄</th>
		<th>职业</th>
	</tr>
	<tr class="tpl">
		<td></td>
		<td></td>
		<td></td>
	</tr>
</table>

样式为display: none

数据:

var tplArr = [
	{
		name: 'jackson',
		age: 25,
		job: 'singer'
	},
	{
		name: 'ben',
		age: 32,
		job: 'teacher'
	}
];
var tplTable = $("table");

tplArr.forEach(function(elem, idx) {
	var cloneDOM = $(".tpl").clone().removeClass("tpl");
	cloneDOM.find("td").eq(0).text(elem.name)
			.next().text(elem.age)
			.next().text(elem.job);
	tplTable.append(cloneDOM);
})

data

设置自定义属性

$("div").data("data-set", "test");

还可以传入json

$("div").data({
    name: "jsckson",
    age: 25
});

取值

$("div").data("name");

在控制台elements上是看不到效果的,只能访问

例:

<div class="container">
	<div class="tpl">
    	<p></p>
    	<p></p>
    	<button>添加</button>
    </div>
    <div class="resArea">
    	总价:<span class="total"></span>
    </div>
</div>
var tplArr = [
	{
		id: "001",
		name: 'jackson',
		shoe: '阿迪达斯',
		price: 2153
	},
	{
		id: "001",
		name: 'ben',
		shoe: '耐克',
		price: 1212
	}
];
//模板
tplArr.forEach(function(elem, idx) {
	var cloneDOM = $(".tpl").clone().removeClass("tpl");
	cloneDOM.data({  //映射数据
		id: elem.id,
		name: elem.name,
		shoe: elem.shoe,
		price: elem.price
	})
		.find("p").eq(0).text(elem.shoe)
		.next().text(elem.price);
	cloneDOM.insertBefore($(".resArea"));
})

$("button").click(function() {
    //隐式类型转化
	$(".total").text(+$(".total").text() + $(this).parent().data("price"));
})

remove 事件无法恢复

$("div").click(function() {
    alert("被点击啦~");
})
$("div").remove().appendTo("body")

remove之后绑定的事件也跟着移除了,再添加回去事件也不在了

detach 事件可以恢复

移除再添加事件还是在

事件

绑定on,解绑off

function handleClick() {
    console.log("click");
}
$("div").on("click", handleClick);
$("div").off("click", handleClick);
$("div").off("click");
$("div").off(); //全部解绑
$("div").on("click", function() {
	alert("被点击了一次");
})
$("div").on("click", function() {
	alert("被点击了两次");
})

如果元素被绑定了多个相同的监听事件,都是会被执行的

还可以传入数据,通过e.data拿到

$("div").on("click", {test: "test"}, function(e) {
	console.log(e.data)
})

事件委托

委托的元素写前面,触发的元素写后面

$("ul").on("click", "li", function(e, idx) {
	console.log($(this).test());
	//拿索引
	var item = $(this),
	    list = document.getElementsByTagName("ul")[0],
	    idx = item.index(list);
})

还可以给特殊项进行绑定

$("ul").on("click", "li:even", handleClick);
$("ul").off("click", "li:even", handleClick);

可以绑定多个事件

$("div").on({
    mouseenter: function() {
        console.log("mouseenter");
    },
    click: function() {
        console.log("click")
    },
    mouseleave: function() {
        console.log(mouseleave);
    }
})

trigger

function handleClick() {
    console.log("click");
}
$("div").on("click", handleClick);
$("div").trigger("click")

触发事件,不是用户点击触发

应用场景:广告自动关闭

如果要让事件触发的时候传递一些额外的参数,触发的时候传递

function handleClick(e, a, b) {
    console.log("click", a, b);
}
$("div").on("click", handleClick);
$("div").trigger("click", ["信息1""信息2"])

trigger还有一个重要的应用场景,就是触发自定义事件

$("div").on("customEvent", function() {
    console.log("这个是自定义事件")
});
$("div").trigger("customEvent")

除了trigger找不到其他方法触发自定义事件了

one

one() 方法为被选元素添加一个或多个事件处理程序,并规定当事件发生时运行的函数。

当使用 one() 方法时,每个元素只能运行一次事件处理程序函数。

<a href="https://www.baidu.com"></a>
$("a").one("click", function() {
    window.open("https://www.taobao.com");
    return false; //阻止默认事件和冒泡
})

hover

鼠标移进和移出,可以合并成一个事件

$("div")
    .on("mouseenter", function() {
        console.log("mouse enter");
    })
    .on("mouseleave", function() {
        console.log("mouse leave");
    })
$("div").hover(function() {
        console.log("mouse enter");
    }, function() {
        console.log("mouse leave");
})

keydown/keyup

$("input").keydown(function() {
    $("input").css("backgroundColor", "green");
});
$("input").keyup(function() {
    $("input").css("backgroundColor", "blue");
});

动画

<div class="container">
	<h1>标题</h1>
	<ul>
		<li>text</li>
		<li>text</li>
		<li>text</li>
	</ul>
</div>
ul{	
	display: none;
}

hide/show/toggle

$("h1")
    .on("mouseenter", function() {
        $(this).next().show();
    })
    .on("mouseleave", function() {
        $(this).next().hide(3000);
    })

里面传参,就会有渐变的效果

设置数字,就是时间

插件 jQuery easing plugin cdn

<script src="https://cdn.bootcdn.net/ajax/libs/jquery-easing/1.4.1/jquery.easing.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>

toggle

$("h1").on("click", function() {
    if ($(this).next().css("display") == "none") {
    	$(this).next().show();
    }else{
    	$(this).next().hide();
    }
})

集显示与隐藏于一身,类似开关的效果

$("h1").on("click", function() {
    $(this).next().toggle(); 
})

fadeIn/fadeOut/fadeToggle/fadeTo 改变opacity

$("h1").on("click", function() {
    if ($(this).next().css("display") == "none") {
    	$(this).next().fadeIn();
    }else{
    	$(this).next().fadeOut();
    }
})
$("h1").on("click", function() {
    $(this).next().fadeToggle(); 
})

fadeTo参数:时间、截止透明度、速度、回调

$("h1").on("click", function() {
    $(this).next().fadeTo(1000, 0.5, "swing", function() {
        conso;e.log("finished")
    }); 
})

slideDown/slideUp/slideToggle

改变的都是纵向的数值

animate(style, speed, easing, callback)

<div class="container"></div>
$(".container").animate({
	width: "200px",
	height: "200px"
}, 5000, "swing", function() {
	console.logo("finished");
})

可以不指定具体数值,让它在原来的基础上增加一定值

$(".container").animate({
	width: "+=50px",
	height: "+=50px"
});

css设置宽高为100,那么150的时候就会停止

还可以在回调函数里进行下一次动画

$(".container").animate({
	width: "+=100px",
	height: "+=100px",
	top: "100px",
	left: "100px"
}, 1000, "swing", function() {
	console.log("第一次动画结束");
	$(this).animate({
		width: "+=20px",
		height: "+=20px"
		top: "0px",
	    left: "200px"
	}, 5000, "swing", function() {
		console.log("第二次动画结束");
	})
})

不支持颜色,可以变化的只有和数值有关的

动画原理:动画队列(queue)

queue() 方法显示或操作在匹配元素上执行的函数队列。

$(".container")
	.queue("test", function() {
		console.log(1)
	})
	.queue("test", function() {
		console.log(2)
	})
	.queue("test", function() {
		console.log(3)
	})
console.log($(".container").queue("test")); //[ƒ, ƒ, ƒ]
$(".container").dequeue("test"); //1

调用 .dequeue(),下一个排队的函数才能执行。

控制台打印

$(".container").queue("test") //[ƒ, ƒ]
$(".container").dequeue("test"); //2

执行完移除

$(".container")
	.queue("test", function(next) {
		console.log(1)
		next();
	})
	.queue("test", function(next) {
		console.log(2)
		next();
	})
	.queue("test", function() {
		console.log(3)
	})
$(".container").dequeue("test");
console.log($(".container").queue("test"));

next方法可以连续执行,不需要dequeue,但是第一次触发还是要执行dequeue的

1
2
3
[]

clearQueue 清理队列

console.log("清理前", $(".container").queue("test"));
$(".container").clearQueue("test");
console.log("清理后", $(".container").queue("test"));
清理前 (3)[ƒ, ƒ, ƒ]
清理后 []

用队列实现上面的动画,其实之前的动画大概就是这么实现的

$(".container")
	.on("click", function() {
		$(this).dequeue("test");
	})
	.queue("test", function(next) {
		$(this).animate({
			width: "+=100px",
			height: "+=100px",
			top: "100px",
			left: "100px"
		})
		next();
	})
	.queue("test", function() {
		$(this).animate({
			width: "+=20px",
			height: "+=20px"
			top: "0px",
		    left: "200px"
		})
	})

暂停动画 stop

<button class="startBtn">开始</button>
<button class="pauseBtn">暂停</button>
<button class="endBtn">结束</button>
<div class="container"></div>

开始

$(".startBtn").on("click", function() {
	$(".container")
		.animate({
			width: "+=100px",
			height: "+=100px",
			top: "100px",
			left: "100px"
		}, 2000)
		.animate({
			width: "+=20px",
			height: "+=20px",
			top: "50px",
		    left: "200px"
		}, 5000);
})

暂停

$(".pauseBtn").on("click", function() {
	$(".container").stop();
})

这里的代码测试发现,小球似乎并没有停,但是每次结束的width和height不一样

所以stop()只是结束当前动画,第一个animate停了,第二个animate还会继续

要想让小球完全停止,只要在stop里传递参数true就可以了

$(".container").stop(true);

这样就可以清空动画队列,变成空数组[],不会有一个个f了

并且这里,除了可以传一个true之外,还可以传两个true

第一个true代表是否清空队列

第二个true代表第一个原本动画结束的点(200,200,100,100),按按钮会立即跳到当前动画的结尾

$(".container").stop(true, true); //停住,瞬移
$(".container").stop(false, true); //继续下一个动画,瞬移

延迟 delay

在两个动画之间可以设置延迟,这样就会等一下再继续下一个动画

$(".startBtn").on("click", function() {
	$(".container")
		.animate({
			width: "+=100px",
			height: "+=100px",
			top: "100px",
			left: "100px"
		}, 2000)
		.delay(3000)
		.animate({
			width: "+=20px",
			height: "+=20px",
			top: "50px",
		    left: "200px"
		}, 5000);
})

jQuery.fx.off 移除过渡效果

jQuery.fx.off = true

没有动画效果,直接变换

尺度位置

offset 相对于文档的位置,和父元素无关

取值赋值皆可

("div").offset({"top": 50, "left": 50});

position 相对于父元素的位置

scrollLeft / scrollTop

窗口滚动条

$(window).scrollTop(); //取值
$(window).scrollTop(500);  //赋值

元素内滚动

<div class="container">
	<div class="item"></div>
</div>
.container {
	width: 150px;
	height: 150px;
	padding: 20px;
	margin: 20px;
	border: 10px solid #424242;
	overflow: auto;
}
.container .item{
	width: 1500px;
	height: 1500px;
	background: orange;
}
$(".container").scrollTop(500);

width / innerWidth(+padding) / outerWidth(+padding + boeder)

$(".container").width(); //150
$(".container").innerWidth(); //190
$(".container").outerWidth(); //210

想要再加上margin值,往outerWidth里传true即可

$(".container").outerWidth(true); //250

工具方法

之前都是实例方法,直接$调用的是工具方法

$.type

对应的原生方法是typeof(),可以判断的类型有String、Number、Boolean、undefined、Object、Function,有缺陷,不利于判断Array、object、null

jQuery的$.type解决了这个问题

typeof([1, 2, 3]); //object
 $.type([1, 2, 3]); //array
 
 typeof(null); //object
 $.type(null); //null
 
 typeof(new Number()); //object
 $.type(new Number()); //number
 
 typeof(new Date()); //object
 $.type(new Date()); //date
 
 typeof(new RegExp()); //object
 $.type(new RegExp()); //regexp
 
 function Car(){};
 typeof(new Date()); //object
 $.type(new Date()); //object

.isArray/.isArray / .isFunction / $.isWindow...

$.trim 去掉头尾空格

和原生的trim结果一样

var str = "   tes  t   ";
str.trim(); //tes  t
$.trim(str); //tes  t

$.proxy 改变this指向

类似于bind

参数:需要改变的方法,改变的this指向

function test() {
    console.log(this);
}
var person = {
    name: "jackson"
}
$.proxy(test, person);
test(); //window

proxy改变this指向并不是这么简单的,实际上proxy会返回一个新函数,在新函数里this指向才是指向Person的,旧函数this还是指向window

var testAfterProxy = $.proxy(test, person);
testAfterProxy(); {name: "jackson"}

所以需要一个变量接收返回的新函数

应用:

var test = {
	init: function() {
		this.elem = document.getElementsByClassName("test")[0];
		this.num = 100;
		this.bindEvent();
	},
	bindEvent: function() {
		this.elem.addEventListener("click", $.proxy(this.showNum, this), false);
	},
	showNum: function() {
		console.log(this.num);
	}
}
test.init();

$.noConflict

jQuery担心其他库也使用$符号,所以设计了这个方法

var $NC = $.noConflict();

NC就可以替代NC就可以替代符号,原来的$符号就不能用了

$.each()

之前的实例方法是$().each

var arr = [1, 2, 3];
$.each(arr, function(idx, elem) {
    console.log(idx, elem);
})

和forEach使用方法一样,甚至$.each()是forEach的前身,比forEach更早,性能来说还是forEach更好,所以一般还是用原生forEach

$.map

和$.each()情况一样,还是用原生比较好

var arr = [1, 2, 3];
var newArr = $.map(arr, function(idx, elem) {
    return elem * 3;
})
console.log(newArr); //[3, 6, 9]

$.parseJSON

var json = '{"name": "jackson"}';
var res = $.parseJSON(json);

和原生的JSON.parse使用方法一样

JSON.stringify在jQuery里没有相对应的方法

$.makeArray

传一个参数的话,可以将类数组转变为数组

var obj = {
    0: "jackson",
    1: "eden",
    2: "dnaiel",
    length: 3
}
console.log($.makeArray(obj)); //["jackson", "eden", "dnaiel"]

传多个参数:

  1. 第二个参数传类数组,就会往类数组里追加对应的项

第一个参数传字符串、数组、布尔值等,都会追加

console.log($.makeArray("ben", obj)); // {0: "jackson", 1: "eden", 2: "dnaiel", 3: "ben", length: 4}
console.log($.makeArray(999, obj)); //{0: "jackson", 1: "eden", 2: "dnaiel", 3: 999, length: 4}

传数组,就会把数组里的内容依次追加到类数组里

var arr = [1, 2, 3];
console.log($.makeArray(arr, obj)); //{0: "jackson", 1: "eden", 2: "dnaiel", 3: 1, 4: 2, 5: 3, length: 6}
  1. 第二个参数传数组,数组追加
console.log($.makeArray(obj, arr)); // [1, 2, 3, "jackson", "eden", "dnaiel"]

总结:返回什么数据类型,看第二个参数

$.extend 扩展,自定义方法

方法内传递对象,设置属性方法

扩展工具方法

$.extend({
	min: function(a, b) {
		return a < b ? a : b;
	}
});
console.logo($.min(15, 10));

扩展实例方法

$.fn就相当于jQuery.prototype

$.fn.extend({
	clickToShowNumber: function() {
		$(this).click(function() {
			alert(($(this).text()) * 2);
		})
	}
});
$("ul li").clickToShowNumber();

浅拷贝,两种扩展都可以

var person1 = {
	name: 'jaskson',
	age: 25,
	sex: 'male'
}
var person2 = {
	name: 'eden',
	age: 8,
	job: 'student',
	bro: {
		name: 'ben'
	}
}

$.extend(person1, person2);

console.log(person1, person2)

extend方法会将person2的值拷贝到person1中,如果有重复就替代掉,要注意的是浅拷贝,引用值指向的是同一个,一个更改了另一个也会更改

如果有三个参数,person2拷贝到person1,得出结果,再被person3覆盖,以此类推

深拷贝,第一个参数传true即可

$.extend(true, person1, person2);

引用值也会拷贝覆盖,但不是浅拷贝的那种覆盖了,而是在person1中创建一个同名对象,然后逐步添加键值对

$.ajax

jsonpPlaceholder API: jsonplaceholder.typicode.com/

$.ajax({
	url: 'http://jsonplaceholder.typicode.com/users',
	type: 'GET',
	dataType: 'JSON', //期待服务器返回的数据类型
	timeout: 1000,
	async: true, //是否异步,默认就是true
	success: function(res) {
		console.log(res);
	},
	error: function(e) {
		console.log(e);
	},
	complete: function() {
		// 无论成功还是失败都会执行这个方法
	},
	context: document.body //改变this指向
})

源码

;(function() {
	function jQuery(selector) {
	    //通过这个函数调用,传入参数,执行init函数,由于init是构造函数,再new实例化一下
		return new jQuery.prototype.init(selector);
	}

	jQuery.prototype.init = function(selector) {
		// this = {}; init函数实例化出来this
		// 按照相应的选择器,找到对应的DOM元素,包装成jQuery对象并返回

		this.length = 0;

		if (selector == null) {
			return this;
		}

		if (typeof selector == 'string' && selector.indexOf('.') != -1) {
			var elem = document.getElementsByClassName(selector.slice(1));//slice去点
		} else if(typeof selector == 'string' && selector.indexOf('#') != -1) {
			var elem = document.getElementById(selector.slice(1));
		}

		if (selector instanceof Element || elem.length == undefined) {
			this[0] = elem || selector;
			this.length ++;
		} else {
			for (var i = 0; i < elem.length; i++) {
				this[i] = elem[i];
				this.length ++;
			}
		}
	}

	// css方法不在jQuery.prototype.init里面,无法调用
	jQuery.prototype.init.prototype = jQuery.prototype;

	jQuery.prototype.css = function(option) {
		for (var i = 0; i < this.length; i++) {
			// this就是init实例化出来的this,里面包含选出来的元素
			for(var attr in option) {
				this[i].style[attr] = option[attr];
			}
		}
		return this; //实现链式调用
	}

	jQuery.prototype.get = function(number) {
		if (number == null) {
			// 空截,不截取,将类数组变成数组
			return [].slice.call(this, 0)
		} else {
			if (number >= 0) {
				return this[number];
			} else {
				// 负数就数字+长度
         		return this[number + this.length];
			}
		}
	}

	jQuery.prototype.eq = function(number) {
		// 返回jQuery对象
		var elem =  number != null
						   ? (number >= 0 ? this[number] : this[number + this.length])
						   : [].slice.call(this, 0);
		// return jQuery(elem); //数组变成类数组
		// 上一步放到pushStack里实现
		return this.pushStack(elem);
	}

	jQuery.prototype.add = function(selector) {
		var curObj = jQuery(selector),
			baseObj = this,
			newObj = jQuery(); //空对象

		for (var i = 0; i < curObj.length; i++) {
			newObj[newObj.length++] = curObj[i];
		}

		for (var i = 0; i < curObj.length; i++) {
			newObj[newObj.length++] = baseObj[i];
		}
		this.pushStack(newObj);
		return newObj;
	}

	jQuery.prototype.end = function() {
		return this.prevObject;
	}

	jQuery.prototype.pushStack = function(elem) {
		if (elem.constructor != jQuery) {
			elem = jQuery(elem);
		}
		elem.prevObject = this;
		return elem;
	}

	window.$ = window.jQuery = jQuery;
})();