jQuery 是什么
- jQuery 本质:是一个快速,轻量级且功能丰富的 JavaScript 方法库,不是常说的前端框架,前端框架一般来说主要还是 VUE、React、Angular。
- jQuery 优点:选择器、隐式迭代、链式编程、解决兼容(jquery1功能,jquery3已经废弃),从而简化了HTML文档的 遍历、事件、动画和Ajax交互,从而实现了快速的Web开发
- jQuery 特色:一行代码,少写多做
- jQuery 官网:jquery.com/
jQuery
基本语法
$(选择器).执行方法();
<head>
<script src="./jquery-3.7.1.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
// 原生 js 写法
const btn = document.getElementById("btn");
btn.onclick = () => {
document.createElement("div");
div.style.width = "200px";
div.style.height = "200px";
div.style.backgroundColor = "red";
document.body.appendChild(div);
}
})
$().ready(() => {
// jquery 写法
$("#btn").click(() => {
$("<div></div>").css({
width: "200px",
height: "200px",
backgroundColor: "red"
}).appendTo($("body"))
})
})
</script>
</head>
<body>
<button id="btn">click</button>
</body>
解决 $ 冲突
noConflict()方法会释放对$标识符的控制,使得$变成undefined
var jq = jQuery.noConflict();
jq(document).ready(function(){
jq("button").click(function(){
jq("p").text("jQuery 仍然在工作!");
});
});
//如果你的 jQuery 代码块使用 $ 简写,并且您不愿意改变这个快捷方式,那么您可以把 $ 符号作为变量传递给 ready 方法。这样就可以在函数内使用 $ 符号了 - 而在函数外,依旧不得不使用 "jQuery"
$.noConflict();
jQuery(document).ready(function($){
$("button").click(function(){
$("p").text("jQuery 仍然在工作!");
});
});
入口函数
- ready:在 html 所有标签(DOM)都加载之后,就会去执行。
- JavaScript 入口函数:
window.onload 事件是等到所有内容,包括外部图片之类的文件加载完后,才会执行,因此如果遇到有图片加载的问题,就会存在页面阻塞。
//jQuery 入口函数
$(document).ready(function(){ /*...*/ })
$(function(){ /*...*/ })
// 这个入口函数是对 js 事件 DOMContentLoaded 的封装,但执行要比 ready 入口函数更快
document.addEventListener("DOMContentLoaded", function (event) {/*...*/})
//JavaScript 入口函数
window.onload = function(){ /*...*/ }
链式调用
jquery 在执行内置方法后,会返回当前的 jQuery 对象本身
// 这里的addClass()、css()和slideDown()方法在执行后都返回$('#myElement'),因此可以被连续调用
$('#myElement').addClass('active').css('color', 'red').slideDown();
选择器
jQuery 选择器返回一个伪数组,包含 length 属性
共同生效
//同 CSS - 常用
$("*"); //-> 所有
$("#idName"); //-> id="idName"
$("div"); //-> 所有 div 标签
$(".intro,.demo"); //-> class 为 "intro" 或 "demo" 的所有元素
$("div:not(p)"); //-> div中所有非p的元素,但如果p没有设置样式也会受到影响
$(":root"); //-> 文档的根元素 html
$(":header"); //-> 所有 h1 - h6,但 h7-h9 也会匹配上
$("div > p"); // div 直接子元素的所有 p
$("div p"); // div 的所有后代 p
$("div + p"); // div 相邻的下一个 p,同jQuery的 next("p") 方法
$("div ~ p"); // div 同级的所有 p,同jQuery的 siblings("p") 方法
$("p:first-child"); //(所有元素中计数)-> 作为某个父元素的 第一个子元素(匹配满足这个条件的 第一个 p)
$("p:first-of-type"); //(所有元素中计数)-> 作为某个父元素的 第一个子元素(匹配满足这个条件的 所有 p)
$("p:last-child"); //(所有元素中计数)-> 作为某个父元素的 最后一个子元素(匹配满足这个条件的 第一个 p)
$("p:last-of-type"); //(所有元素中计数)-> 作为某个父元素的 最后一个子元素(匹配满足这个条件的 所有 p)
$("p:nth-child(2)"); //(所有元素中计数)-> 作为某个父元素的 第二个子元素(匹配满足这个条件的 所有 p)
$("p:nth-last-child(2)"); //(所有元素中计数)-> 作为某个父元素的 倒第二个子元素(匹配所有满足这个条件的 所有 p)
$("p:only-child"); //(所有元素中计数)-> 作为某个父元素的 p 元素(p是唯一的子元素,且只有一个)
$("p:nth-of-type(2)"); //(只在p元素中计数)-> 作为某个父元素的 所有第二个 p 元素
$("p:nth-last-of-type(2)"); //(只在p元素中计数)-> 作为某个父元素的 所有倒第二个 p 元素
$("p:only-of-type"); //(只在p元素中计数)-> 作为某个父元素的 那个唯一的 p 元素
$("p:lang(de)"); // 所有 lang 属性值为 "de" 的 p 元素
$("[href]"); // 所有带有 href 属性的元素
$("[href='default.htm']"); // 所有带有 href 属性且值等于 "default.htm" 的元素
$("[href!='default.htm']"); // 所有带有 href 属性且值不等于 "default.htm" 的元素
$("[href$='.jpg']"); // 所有带有 href 属性且值以 ".jpg" 结尾的元素
$("[title^='Tom']"); // 所有带有 title 属性且值以 "Tom" 开头的元素
$("[title|='Tomorrow']"); // 所有带有 title 属性且值等于 'Tomorrow' 或者以 'Tomorrow' 后跟连接符作为开头的字符串
$("[title~='hello']"); // 所有带有 title 属性且值包含单词 "hello" 的元素
$("[title*='hello']"); // 所有带有 title 属性且值包含字符串 "hello" 的元素
$("input[id][name$='man']"); // 带有 id 属性,并且 name 属性以 man 结尾的 input
$("a:link"); //-> 所有未访问链接
$("#news:target"); //-> 当前活动的#news元素(包含该锚名称的点击的URL)
$(":empty"); //-> 所有空元素(不含子元素或文本)
//同 CSS - 表单常用
$(":input"); //-> 所有 input 元素
$(":text"); //-> 所有带有 type="text" 的 input 元素
$(":password"); //-> 所有带有 type="password" 的 input 元素
$(":radio"); //-> 所有带有 type="radio" 的 input 元素
$(":checkbox"); //-> 所有带有 type="checkbox" 的 input 元素
$(":submit"); //-> 所有带有 type="submit" 的 input 元素
$(":reset"); //-> 所有带有 type="reset" 的 input 元素
$(":button"); //-> 所有带有 type="button" 的 input 元素
$(":image"); //-> 所有带有 type="image" 的 input 元素
$(":file"); //-> 所有带有 type="file" 的 input 元素
$("input:not(:empty)"); //-> 所有不为空的input
$(":focus"); //-> 当前 焦点的元素
$(":enabled"); //-> 所有启用的元素
$(":disabled"); //-> 所有禁用的元素
$(":selected"); //-> 所有选定的(option 具有 selected 属性)下拉列表元素
$(":checked"); //-> 所有选中的复选框选项
$("input:in-range") //标签的值在指定区间之外时显示的样式,例如 type="number" value="11" min="10" max="12
$("input:out-of-range");//标签的值在指定区间之外时显示的样式,例如 type="number" value="15" min="10" max="12
$("input:valid") //用于匹配输入值为合法的元素
$("input:invalid") //用于匹配输入值为非法的元素
$("input:optional") //用于匹配可选的输入元素
$("input:required") //匹配设置了 "required" 属性的元素
仅 CSS
//CSS 有效,jQuery无效
$(":visited"); //无效:选择所有访问过的链接
$(":active"); //无效:选择活动链接
$(":hover"); //无效:选择鼠标在链接上面时
$("p:first-letter"); //无效:所有 p 的第一个字母
$("p:first-line"); //无效:所有 p 的第一行 1
$("p:before"); //无效:在每个<p>元素之前插入内容 2
$("p:after"); //无效:在每个<p>元素之后插入内容
$("p::selection"); //无效 匹配元素中被用户选中或处于高亮状态的部分的属性 color, background, cursor,和outline
$("input:ready-write"); //无效 标签的值在指定区间之外时显示的样式,例如 type="number" value="15" min="10" max="12
$("input:ready-only"); //无效 标签的值在指定区间之外时显示的样式,例如 type="number" value="15" min="10" max="12
仅 JQuery
//jQuery 特有
$("p:first"); //-> 第一个 <p> 元素
$("p:last"); //-> 最后一个 <p> 元素
$("tr:even"); //-> 从 0 开始,所有偶数 tr 元素
$("tr:odd"); //-> 从 1 开始,所有奇数 tr 元素
$("p:eq(3)"); //-> 第4个p(从0开始计数)
$("p:gt(3)"); //-> 第3个之后的所有 p
$("p:lt(3)"); //-> 第3个之前的所有 p
$(":animated"); //-> 所有使用了animate()方法的动画元素,css定义的动画不匹配
$(":contains('ok')"); //-> 所有包含文本 "ok" 的元素
$("div:has(p)"); //-> 后代元素中包含 p 的 div
$(":parent"); //-> 所有非空元素(父元素,含有子元素或者文本,区别表单的空)
$("p:hidden"); //-> 所有隐藏的 p -> (隐藏指:display:none 或 type="hidden"表单,该选择器对 visibility:hidden 和 opacity: 0 的元素不起作用)
$("p:visible"); //-> 所有可见的 p -> (可见是非以下情况: (1)display:none (2)type="hidden" 表单 (3)width 和 height 设置为 0)
元素节点
获取
父级
$(selector).parent(filter)返回直接父级$(selector).parents(filter)返回所有祖先元素,一路向上直到文档的根元素 html$(selector).parentsUntil(filter)返回介于两个给定元素之间的所有祖先元素
子级
$(selector).children(filter)返回被选元素的所有直接子元素$(selector).find(filter)返回与指定参数匹配的所有后代元素
同级
$(*selector*).index(element)返回指定元素相对于其他指定元素的 index 位置,如果未找到元素,index() 将返回 -1$(*selector*).slice(*start,stop*)slice() 方法选取基于索引的元素的子集,参数 start(必需),规定开始选取元素的位置,索引号从 0 开始,如果是负数,从末端开始选,stop(可选),结束位置,如果省略,则末端结束,索引号从 0 开始,如果是负数,从末端开始$(selector).siblings(filter)返回所有同级元素(selector 如果是同一级,则不包含自身;selector 如果是先指向父类,然后指向后代,则会返回所有)$(selector).next(filter)$(selector).prev(filter)next() 返回同级的下一个元素;prev()方向相反$(selector).nextAll(filter)$(selector).prevAll(filter)nextAll() 返回后面同级的所有元素;prevAll()方向相反$(selector).nextUntil(filter)$(selector).prevUntil(filter)nextUntil() 返回介于两个给定参数之间的后面所有的同胞元素,prevUntil() 方向相反
过滤
$(selector).first(filter)$(selector).last(filter)返回选择器指定的首个/最后一个元素(不包括 selector 自身)$(selector).eq(filter)返回选择器指定的第 n 个元素,索引号从 0 开始$(selector).filter(filter)$(selector).not(filter)filter 返回匹配的;not 返回不匹配的;具体规则可以自由指定;not() 方法与 filter() 相反。
$("p").parent(); //返回 p 的直接父级
$("p").parent(".selected"); //返回带有 selected 类的 p 的直接父元素
$("span").parents(); //返回所有 <span> 元素的所有祖先
$("span").parents("ul"); //返回所有 <span> 元素的所有祖先,并且它是 <ul> 元素
$("span").parentsUntil("div"); //返回介于 <span> 与 <div> 元素之间的所有祖先元素
$("div").children(); //返回DIV的直接子元素
$("div").children("p"); //返回DIV的直接子代的所有<p>元素
$("p").find("span"); //返回属于 p 后代的所有 span 元素
$("div").find("*"); //返回 div 的所有后代
$("h2").siblings();//返回 <h2> 的所有同胞元素
$("h2").siblings("p"); //返回属于 <h2> 的同胞元素的所有 <p> 元素
$("h2").next(); //返回 <h2> 的下一个同胞元素
$("h2").nextAll(); //返回 <h2> 的所有跟随的同胞元素
$("h2").nextUntil("h6"); //返回介于 <h2> 与 <h6> 元素之间(不包括 h2 和 h6)的所有同胞元素
<li>111</li> <li>222</li> <li>333</li> <li>444</li>
Array.from($("li").nextAll("li:nth-of-type(3)")).map(_=>_.innerText); // [333]
Array.from($("li").nextUntil()).map(_=>_.innerText); // [222,333,444] -> 从选择器的第二个开始到末尾
Array.from($("li:nth-of-type(1)").nextUntil("li:nth-of-type(3)")).map(_=>_.innerText); // [222]
Array.from($("li").prevAll()).map(_=>_.innerText); // [333,222,111] -> 从选择器的倒数第二个往上/往前,直到最上面/前面那个
Array.from($("li").prevUntil()).map(_=>_.innerText); // [333,222,111] -> 从选择器的倒数第二个往上/往前,直到给定选择器条件的下一个(如果不指定,则会到底)
Array.from($("li").prevUntil("li:nth-of-type(2)")).map(_=>_.innerText); // [333] -> 从选择器倒数第二个开始,往上/往前,直到给定选择器条件的最前面一个
$("li").click(function(){ console.log($(this).index()) }); //获取当前点击的下标
console.log($(".hot").index($("#favorite"))); //获取class为hot的元素中id为favorite的下标
$("p").slice().css("background-color","yellow"); //标黄所有 <p> 元素
$("p").slice(2).css("background-color","yellow"); //标黄索引 2 以及后面的 <p> 元素
$("p").slice(2,4).css("background-color","yellow"); //标黄索引 2 到索引 4 的 <p> 元素
$("li").first()[0].innerText; //111
$("li").last()[0].innerText; //444
$("li").eq(2)[0].innerText; //333
$("p").filter(".url"); //返回带有类名 "url" 的所有 <p> 元素
$("p").not(".url"); //返回不带有类名 "url" 的所有 <p> 元素
//not 和 eq 可以实现反选的效果,选取索引值不为 1 的 p 元素,并把背景颜色设置为黄色
$("p").not(":eq(1)").css("background-color","yellow");
内容
- html() - innerHTML 设置或返回所选元素的内容(包括 HTML 标签),获取的时候只获取第一个,设置的时候会隐式迭代
- text() - innerText 设置或返回所选元素的文本内容,获取和设置的时候都会隐式迭代
- val() - value 设置或返回表单字段的值,返回第一个输入字段的值, 获取的时候只获取第一个,设置的时候会隐式迭代
- 回调函数的参数说明:以上三者都拥有回调函数,且有 两个参数
- 参数 1:被选元素列表中 当前元素的下标
- 参数 2:初始值
- 回调函数返回:一个字符串,相当于放在方法中设置的值
//获取
$("#test").text()); //等于 innerText
$("#test").html()); //等于 innerHTML
$("#test").val()); //获取表单 value
$("li").html(); //list
$("li").text(); //listlistlistlistlist
$("input").val();//content
//设置
$("#test1").text("Hello world!");
$("#test2").html("<b>Hello world!</b>");
$("#test3").val("Hello world!");
//携带回调函数
<ul>
<li>111 <button>btn</button></li>
<li>222 <button>btn</button></li>
</ul>
$("li").text(function (index, originalText) {
return `第${index}个元素的初始值为${originalText},回调函数return的结果作为新值放在方法中`;
});
//页面更新为:
// 第0个元素的初始值为111 btn,回调函数return的结果作为新值放在方法中
// 第1个元素的初始值为222 btn,回调函数return的结果作为新值放在方法中
$("li").html(function (index, originalText) {
return `第${index}个元素的初始值为${originalText},回调函数return的结果作为新值放在方法中`;
});
//页面更新为:
// 第0个元素的初始值为111 btn(这个btn被解析成了元素),回调函数return的结果作为新值放在方法中
// 第1个元素的初始值为222 btn(这个btn被解析成了元素),回调函数return的结果作为新值放在方法中
<input type="text" value="10" />
<input type="text" value="11" />
$("input").val(function(index, originalText){
return(`第${index}个元素的初始值为${originalText},回调函数return的结果作为新值放在方法中`);
});
//页面input输入框的内容更新为回调函数的返回值
属性
- attr() - 一般操作 自定义属性,但也能操作原生属性,只是不推荐
- prop() - 一般操作 原生属性,如果非要操作自定义属性,会把自定义属性添加到原生属性上,后面操作获取就是通过对象获取的
- removeAttr() - 移除属性,可使用字符串,如果移除多个属性,在一个字符串中用空格隔开,不能使用回调函数
- 回调函数的参数说明:attr() prop() 都拥有回调函数,且有 两个参数
- 参数 1:被选元素列表中 当前元素的下标
- 参数 2:初始值
- 回调函数返回:一个字符串,相当于放在方法中设置的值,这个回调函数是逐个地对集合元素采取操作,如果是要获取整个集合,把每个元素组合起来当成整理看待,则需要跳出这个函数外,再从第三方进行操作(见示例-多行关键词菜单)
//设置和获取自定义属性
$("input").attr("data-index","0");
$("input").attr("data-index"); // 0 当该方法用于**返回**属性值,只返回第一个匹配元素的值
$("input").attr("data-empty"); // undefined 不存在的属性返回 undefined
$("a").attr({
"data-href" : "http://jquery.com",
"data-index" : "0" }
);
$("a").attr("data-href", function(index,origValue){
return origValue + "/jquery";
});
//设置和获取原生属性
//说明:与DOM中setAttribute()不同的是,prop("disabled",true/false) 有效
t($("input").prop("disabled",true)); //禁止有效
t($("input").prop("disabled")); //true 返回第一个匹配元素的值
t($("input").prop("disabled",false)); //解禁有效
t($("input").prop("disabled")); //false
$("input").prop("disabled",function(index, originalValue){
console.log(`第${index}个元素的初始值为${originalValue},回调函数return的结果作为新值放在方法中`);
return true;
});
//页面结果:input被禁用了
//移除属性
$("input").removeAttr("min max"); //解除input的输入限制
插入
- append() - 在被选元素的结尾插入内容(元素内部)
- appendTo() - 将被选元素的插入到另外一个元素内(元素内部)
- prepend() - 在被选元素的开头插入内容(元素内部)
- after() - 在被选元素之后插入内容(元素外面)
- before() - 在被选元素之前插入内容(元素外面)
- remove() - 删除被选元素(及其子元素),扔掉桶的做法,可传参实现过滤删除
- empty() - 从被选元素中删除子元素,倒掉水的做法
- clone() - 方法生成被选元素的副本,包含子节点、文本和属性
//append() 和 prepend() 方法能够通过参数接收无限数量的新元素
function appendText(){
var txt1="<p>文本-1</p>"; // 使用 HTML 标签创建文本
var txt2=$("<p></p>").text("文本-2"); // 使用 jQuery 创建文本
$("body").append(txt1,txt2,"<p>文本-3</p>"); // 追加新元素
}
//after() 和 before() 方法能够通过参数接收无限数量的新元素
function afterText() {
var txt1="<b>I </b>"; // 使用 HTML 创建元素
var txt2=$("<i></i>").text("love "); // 使用 jQuery 创建元素
$("img").after(txt1,txt2,"<b>you</b>"); // 在图片后添加文本
}
//删除
$("#div1").remove(); //删除被选元素及其子元素
$("p").remove(".italic"); //过滤:删除除了类名为italic的p元素,remove是删除自身,因此过滤器中条件只能作用于同级,不能作用于子元素
$("#div1").empty(); //删除被选元素的子元素
//复制
$("body").append($("p:first").clone(true)); //true 复制事件处理程序,alse(默认)不复制事件处理程序
样式
- css() - 设置或返回样式属性
- addClass() - 向被选元素添加一个或多个类
- removeClass() - 从被选元素删除一个或多个类
- toggleClass() - 对被选元素进行添加/删除类的切换操作
- hasClass() - 判断是否包含某个类(如果是多个元素,只要有一个满足就返回 true)
- 说明:
- css() 方法:样式都添加在行内属性;具有隐式迭代功能。例如,选中多个元素,会自动给每个设置好样式
- css() 方法:不能直接设置部分伪类伪元素样式,可通过
$(选择器).append("<style>选择器::after{display:none}</style>");方式间接实现 - 操作 class:能够隐式迭代;操作 class 的方法,添加到 class 中定义的属性,因此相比于 css 将属性添加到行内,权重更低
//获取和设置 css 样式
$("p").css("background-color");
$("p").css("background-color","yellow");
$("p").css({
"background-color":"yellow",
"font-size":"200%"
});
$("div").css({
width:"-=30px",
height:"+=10px" //可以取相对值,在原来的基础上增加或减少
});
//添加多个类
$("body div:first").addClass("important blue");
//第一个参数表示要添加或删除的类,既可以用类列表,也可以用函数返回值指定(i 是选择器选中的所有元素中当前对象的索引值,c 是当前对象的类名)
// switch: 布尔值,true 表示只添加,false 表示只删除
$("body div:first").addClass('c1 c2 ...' | function(i, c)); // 添加一个或多个类
$("body div:first").removerClass('c1 c2 ...' | function(i, c)); // 删除一个或多个类。
$("body div:first").toggleClass('c1 c2 ...' | function(i, c), switch); // 切换一个或多个类
$("li").eq(1).addClass("before").siblings().removeClass("after"); //指定修改 li 元素中第二个的class样式
$("li").removeClass("before").eq(2).addClass("after"); //指定修改 li 元素中第三个的class样式
尺寸
- width() height() - content:设置或返回元素的宽/高(不包括内边距、边框或外边距),类似 DOM 中的 offsetWidth offsetHeight,但在 display: none; 时,依然能获取到
- innerWidth() innerHeight() - content+padding:返回元素的宽高(包括内边距),类似 DOM 中的 clientWidth clientHeight
- outerWidth() outerHeight() - content+padding+border:返回元素的宽高(包括内边距和边框)
- outerWidth(true) outerHeight(true) - content+padding+border+margin:返回元素的宽高(包括内边距、边框和外边距)
- 注意:
- 如果设置了 box-sizing 后,width() 获取的是 css 设置的 width 减去 padding 和 border 的值
- 即使设置了 display: none;,依然能获取到

$("li").width();
$("li").eq(1).height();
//对于设置了 box-sizing
//.test{width:100px;height:100px;padding:10px;border:10px;box-sizing:border-box;}
$(".test").width(); // 60
$(".test").innerWidth(); // 80
$(".test").outWidth(); // 100
布局
- offset() - 获取距离文档流左上角的 left、top,如果传参数就是设置,相当于是在行内添加了一个 relative 的相对定位样式
- position() - 有定位的元素的 left、top(相对于它的父元素),只能获取不能设置,该方法返回一个带有两个属性(以像素为单位的 top 和 left 位置)的对象
- 说明:如果有多个元素,只获取第一个
//返回偏移坐标:$(*selector*).offset()
$("p").offset();
$("li").offset(); //{top: 8, left: 48}
$("li").eq(1).offset(); //{top: 29, left: 48}
//设置偏移坐标:$(*selector*).offset({top:*value*,left:*value*})
$(".relative .absolute").offset(); //{top: 329, left: 208}
$(".relative .absolute").offset({left:50,top:50});
$(".relative .absolute").offset(); //{top: 50, left: 50}
newPos=new Object();
newPos.left="0";
newPos.top="100";
$("p").offset(newPos);
//使用函数设置偏移坐标:$(*selector*).offset(function *(index,currentoffset)* )
$("p").offset(function(n,c){
newPos=new Object();
newPos.left=c.left+100;
newPos.top=c.top+100;
return newPos;
});
//获取 <p> 元素在 <div> 元素内部的位置
<div style="border:1px solid black;padding:100px;margin:50px;">
<p>这一段的位置(相对于它的父元素)是<span id="span1">x</span> top 和 <span id="span2">x</span> left.</p>
</div>
x=$("p").position();
$("#span1").text(x.top);
$("#span2").text(x.left);
动画效果
显隐动画
- hide() - 隐藏;
- show() - 显示;
- toggle() - 切换;
- 底层以
display:none/block;实现 - 语法:
$(*selector*).hide(speed,timing-function,callback); - 可选参数 speed:隐藏/显示的速度,可以三类值:slow、fast、毫秒
- 可选参数 timing-function:字符串,效果过渡类型,只能取值 linear ,其它报错
- 可选参数 callback:隐藏或显示完成后所执行的函数,如果调用 callback 时,在函数名后直接加括号,会立刻执行函数体,而不是等到显示/隐藏完成后才执行,而且只会 执行一次,只有当作回调参数的时候才会有多个元素而 执行多次
$("div").hide(1000,"linear",function(){
alert("Hide() 方法已完成!");
});
渐变动画
- fadeOut() - 淡出;
- fadeIn() - 淡入;
- fadeToggle() - 切换;
- fadeTo() - 指定渐变;
- 底层以
display:none/block;和opacity:[number];实现 - 语法:
$(*selector*).fadeToggle(*speed,timing-function,callback*);$(*selector*).fadeTo(speed,opacity,timing-function,callback); - 参数:fadeTo() 没有默认参数,必须加上 slow/fast/Time
- 可选参数 speed:隐藏/显示的速度,可以三类值:slow、fast、毫秒
- 可选参数 opacity:fadeTo() 方法,表示给定的不透明度(值介于 0 与 1 之间)
- 可选参数 timing-function:运动曲线
- 可选参数 callback:隐藏或显示完成后所执行的函数,如果调用 callback 时,在函数名后直接加括号,会立刻执行函数体,而不是等到显示/隐藏完成后才执行,而且只会 执行一次,只有当作回调参数的时候才会有多个元素而 执行多次
<div style="background-color:black;width:300px;height:300px;display:none;"></div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$("div").fadeIn("slow","linear",function(){console.log("fadeIn 执行结束")}); //fadeIn 执行结束
$("div").fadeOut("slow","linear",function(){console.log("fadeOut 执行结束")}); //fadeOut 执行结束
$("div").fadeToggle("slow","linear",function(){console.log("fadeToggle 执行结束")}); //fadeToggle 执行结束
$("div").fadeTo("slow",0.5,"linear",function(){console.log("fadeTo 方法的四个参数:执行速度、透明度、运动曲线、执行完毕后调用的回调函数")});
</script>
折叠动画
- slideDown() - 下滑;
- slideUp() - 上滑;
- slideToggle() - 切换;
- 底层以
display:none/block;和opacity:[number];实现 - 语法:
$(*selector*).slideToggle(*speed,timing-function,callback*); - 可选参数 speed:隐藏/显示的速度,可以三类值:slow、fast、毫秒
- 可选参数 timing-function:运动曲线
- 可选参数 callback:隐藏或显示完成后所执行的函数,如果调用 callback 时,在函数名后直接加括号,会立刻执行函数体,而不是等到显示/隐藏完成后才执行,而且只会 执行一次,只有当作回调参数的时候才会有多个元素而 执行多次
$("button").click(function () {
$("div").slideToggle(function () {
console.log("animate 动画结束")
});
});
自定义动画
- animate() 创建自定义动画
- 语法:
$(*selector*).animate({params},speed,timing-function,callback); - 必需的 params 参数:定义形成动画的 CSS 属性
- 可选的 speed 参数:规定效果的时长,取值:"slow"、"fast" 或毫秒
- 可选的 timing-function 参数:运动曲线
- 可选的 callback 参数:是动画完成后所执行的函数名称
- 属性名:当使用 animate() 时,必须使用 Camel 标记法书写所有的属性名,比如,必须使用 paddingLeft 而不是 padding-left,使用 marginRight 而不是 margin-right,等等。
- 颜色插件:色彩动画并不包含在核心 jQuery 库中,如需生成颜色动画,需要从 jquery.com 下载 颜色动画 插件。
- 相对值:在值的前面加上 += 或 -=
- 预定义值:把属性的动画值设置为 "show"、"hide" 或 "toggle"
//把 <div> 元素往右边移动 250px
$("div").animate({left:'250px'});
//动画使用多个属性
$("div").animate({ left:'250px', opacity:'0.5', height:'150px', width:'150px' });
//动画可以定义相对值(该值相对于元素的当前值)。需要在值的前面加上 += 或 -=
$("div").animate({ left:'250px', height:'+=150px', width:'+=150px' });
//预定义值:把属性的动画值设置为 "show"、"hide" 或 "toggle",就是 0 到 应有高度 之间切换
$("div").animate({ height:"toggle" });
// 动画队列
var div=$("div");
div.animate({height:'300px',opacity:'0.4'},"slow");
div.animate({width:'300px',opacity:'0.8'},"slow");
div.animate({height:'100px',opacity:'0.4'},"slow");
div.animate({width:'100px',opacity:'0.8'},"slow");
div.animate({left:'100px'},"slow");
div.animate({fontSize:'3em'},"slow");
// 链式调用
div.animate({ height: '300px', opacity: '0.4' }, "slow").
animate({ width: '300px', opacity: '0.8' }, "slow").
animate({ height: '100px', opacity: '0.4' }, "slow").
animate({ width: '100px', opacity: '0.8' }, "slow").
animate({ left: '100px' }, "slow").
animate({ fontSize: '3em' }, "slow");
<button>animate</button>
<div style="background-color:black;width:100px;height:100px;"></div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script type="text/javascript">
$("button").click(function () {
$("div").animate({
width: "200px",
height: "200px",
"background-color": "pink", //颜色无效
transform: "rotate(45deg)", //transform 无效
display: "block"
}, 1000, "linear", function () {
console.log("animate 动画结束")
});
});
</script>
过程控制
- stop() - 停止动画
- finish() - 立即完成当前动画,并清除后续排队的动画,与
.stop(true, true)方法类似 - delay() - 延迟动画
- 语法:
$(*selector*).stop(stopAll,goToEnd); - 可选 stopAll 参数:规定是否应该清除动画队列。默认是 false(仅停止活动的动画,允许任何排入队列的动画向后执行)
- 可选 goToEnd 参数:规定是否立即完成当前动画。默认是 false(不立即完成)
- 该方法,不同的是,finish() 也会引起所有排队动画的 CSS 属性停止。
$("#panel").stop(); //动画中途暂停,还可以继续执行
$("#panel").stop(true); //队列中的动画都清除,动画中途结束,不可继续执行
$("#panel").stop(false,true); //当前动画立即完成,后续队列不清除,还可继续执行
//stop() 停止动画 -> 可用于模拟防抖
ele.stop().toggle();
// 延迟下一个动画 2 秒
$("#panel").dalay(2000).animate({width:200px}, "fast")
事件操作
- ready() - 规定当 DOM 完全加载时要执行的函数
- on() - 绑定事件
- off() - 解绑事件
- one() - 一次性事件,触发后立即解绑
- bind() - 1.7 版本被弃用
- hover() -
元素集合.hover(移入时触发的函数, 移出时触发的函数)如果只传一个函数,会在移入移出的时候都触发 - trigger() - 手动触发事件,会触发默认行为,可以操作 jQuery 对象匹配的所有元素,没有返回值
- triggerHandler() - 不会触发事件的默认行为,不会在 DOM 树中冒泡,只影响第一个匹配元素,返回最后一个事件处理函数的返回值,如果没有处理函数被执行,则返回
undefined $(this):JQuery 中 this 一般用$(this)代替,因为 this.innerHTML 跟$(this).html() 的结果是一致的- 同时给多个元素绑定事件:
$(".brand-detail li,.size-detail li,.color-detail li").on( ... )
/* 绑定事件 on() */
//基础绑定
$("li").on("click",_=> console.log(_.target));
//事件委托绑定:第二个参数表示指定冒泡到哪一个节点(只触发这个节点)
$("ul").on("click","button",function(){ console.log(this); }); //this 的结果是 button节点
//事件参数多个:如果第二个往后的参数是字符串,则默认是冒泡触发事件的一个元素,如果传入的是对象,则认为是作为事件函数的参数
$("ul").on("click","button",{ result:"this is button" },function(e){
console.log(e.data.result, this);
});
/* 解绑事件 off() */
$("ul").off("click"); //移除 click 事件
$("ul").off(); //移除所有的事件
$("ul").off("input",funcA); //解绑链式绑定中指定的一个
/* 触发一次 one() */
$("ul").one("click","button",{ result:"this is button" },function(e){
console.log(e.data.result, this);
})
//链式绑定
$("button").click({arg:"something"},function(){
console.log("clicked");
}).mouseout(function(){
console.log("moved out");
})
$("input").on("input",function funcA(e){
console.log($("input")[0].value);
}).on("input",{inputText:`${$("input")[0].value}`},function funcB(e){
console.log(e.data.inputText);
});
//通过代码的形式自动触发事件
//在 1s 后触发点击事件
$("li").on("click", function(){
console.log("click li");
})
setTimeout(() => {
$("li").trigger("click");
}, 1000);

Ajax 请求
$.ajax()
//高德API获取天气情况
$.ajax({
url: "https://restapi.amap.com/v3/weather/weatherInfo",
method: "get",
data: {
key: "429951164744f19f085244582f537008",
city: "成都",
extensions: "all"
},
dataType: "json",
success: function (result, status, xhr) {
result.forecasts[0].casts.forEach(item => {
console.log(`${status} ${item.date} ${item.dayweather} ${item.daytemp}`)
// success 2025-01-09 多云 12
// success 2025-01-10 小雨 8
// success 2025-01-11 多云 12
// success 2025-01-12 阴 9
})
},
error: function (xhr, status, error) {
console.log(status); //error
console.log(error); //Not Found
}
})
$.get()
两种在客户端和服务器端进行请求-响应的常用方法是:GET 和 POST,具体区别见 详解
- 语法:
$.get(URL,callback);或$.get( URL, data, callback, dataType) - 参数 URL:请求的服务端地址
- 可选参数 data:发送给服务器的字符串或 key/value 键值对对象
- 可选参数 callback:请求成功后执行的回调函数;callback 参数包含三种:responseTxt - 结果内容;statusTXT - 调用状态;xhr - XMLHttpRequest 对象
- 可选参数 dataType:从服务器返回的数据类型。例如:xml, json, script, html
$.get(
"http://localhost:3000/list",
"name=James",
function(responseText,statusText) {
console.log(responseText,statusText);
},
"html"
);
// 以html格式返回
$.get(
"http://localhost:3000/list",
{
name: "James"
},
function(responseText,statusText) {
console.log(responseText,statusText);
},
"json"
);
// 以json格式返回
$.post()
- 语法:
$.post(URL,callback);或$.post( URL, data, callback, dataType) - 参数 URL:请求的服务端地址
- 可选参数 data:发送给服务器的字符串或 key/value 键值对对象
- 可选参数 callback:请求成功后执行的回调函数;callback 参数包含三种:responseTxt - 结果内容;statusTXT - 调用状态;xhr - XMLHttpRequest 对象
- 可选参数 dataType:从服务器返回的数据类型。例如:xml, json, script, html
$.post(
"http://localhost:3000/list",
"name=James",
function(responseText,statusText) {
console.log(responseText,statusText);
},
"html"
);
$.post(
"http://localhost:3000/list",
{name:"James"},
function(responseText,statusText) {
console.log(responseText,statusText);
},
"json"
);
钩子函数
ready()
所有的DOM节点加载完成后执行
//jQuery 入口函数
$(document).ready(function(){ /*...*/ })
$(function(){ /*...*/ })
// 这个入口函数是对 js 事件 DOMContentLoaded 的封装,但执行要比 ready 入口函数更快
document.addEventListener("DOMContentLoaded", function (event) {/*...*/})
//JavaScript 入口函数
window.onload = function(){ /*...*/ }
load()
- load() - 从服务器 加载数据,并把返回的数据 放入被选元素
- 语法 -
$(selector).load(URL, data, callback); - 参数 URL - 地址、文件、锚点
- 参数 data - 查询键/值字符串
- 参数 callback - load() 方法完成后所执行的函数名称
- callback 参数 包含三种:responseTxt - 结果内容;statusTXT - 调用状态;xhr - XMLHttpRequest 对象
// 地址
$("div").load("http://localhost:3000/list","name=James")
// 文件
$("div").load("test.html")
// 锚点
$("div").load("test.html#target")
//callback 参数
$("#div1").load("test.html",function(responseTxt,statusTxt,xhr){
if(statusTxt=="success") alert("外部内容加载成功!");
if(statusTxt=="error") alert("Error: "+xhr.status+": "+xhr.statusText);
});
ajaxStart()
当前页面,第一个 Ajax 发送之前执行
ajaxSend()
每一个 Ajax 发送之前执行
ajaxSuccess()
每一个 Ajax 成功都会触发一次
ajaxError()
每一个 Ajax 失败后都会触发一次
ajaxComplete()
每一个 Ajax 执行完成后都会触发一次,不论成功还是失败
$(window).ajaxStart(function(){
console.log("当前页面作用域,第一个Ajax发送之前执行");
console.log("显示loading");
$("#loading").css("display","block");
});
$(window).ajaxSend(function(){
console.log("每一个Ajax发送之前执行");
console.log("显示loading");
});
$(window).ajaxSuccess(function(){
console.log("每一个Ajax成功都会触发一次");
console.log("隐藏loading...(如果只有一个ajax请求)");
});
$(window).ajaxError(function(){
console.log("每一个Ajax失败后都会触发一次");
});
$(window).ajaxComplete(function(){
console.log("每一个Ajax执行完成后都会触发一次,不论成功还是失败");
})
ajaxStop()
所有 AJAX 请求已完成,当前页面作用域的最后一个Ajax成功
$(document).ajaxStop(function(){
console.log("所有 AJAX 请求已完成,当前页面作用域的最后一个Ajax成功");
console.log("隐藏loading");
$("#loading").css("display","none");
}
);`
when()
相当于 promise 的 finally 函数
var statusVal;
$.ajax({
url: "http://localhost:3000/size",
method: "get",
dataType: "json",
data: {},
success: function (result, status) {
result.forEach(item => {
$(".brand-detail").append(`<li>${item.content}</li>`)
});
statusVal = status;
console.log(statusVal); //true
}
})
// 根据 status 的状态 进行后续操作
function(){
if(statusVal){ //false
// 后续操作
}
}
var statusVal;
var aj = $.ajax({
url: "http://localhost:3000/size",
method: "get",
dataType: "json",
data: {},
success: function (result, status) {
result.forEach(item => {
$(".brand-detail").append(`<li>${item.content}</li>`)
});
statusVal = status;
console.log(statusVal); //true
}
})
// 根据 statusVal 后续操作
function(){
// myajax 请求完毕时执行
$.when(aj).done(function(){
if(statusVal){
// 后续操作
}
}) ;
}
插件
jq 对象的扩展
$.extend( [deep ], target, object1, ..., objectN ) 将一个或多个对象的内容合并到目标对象
- deep:可选,Boolean 类型,是否深度合并对象,默认为 false
- target:Object 类型,目标对象,其他对象的成员属性将被附加到该对象上。
- object1:可选,Object 类型,第一个被合并的对象。
- objectN:可选,Object 类型,第 N 个被合并的对象。
// 扩展一个单纯的方法
$.extend({
lg(val) {
console.log(val)
}
})
$.lg("text content") // text content
// 扩展一个对象的属性
const obj1 = { name: "James" };
const obj2 = { age: 25 };
$.extend(obj1, obj2)
console.log(obj1); // {name: 'James', age: 25}
// 实现深拷贝
// 1. 如果第一个参数为true,表示深拷贝;
// 2. 从第二个参数(或第三个参数)开始,都属于原对象,将要拷贝给第一个参数(或第二个参数)
// 3. 如果是 undefined 则不会拷贝
//浅拷贝
$.extend(objCopy, objOri, { other:"other thing"} )
//深拷贝
$.extend(true, objCopy, objOri, { other:"other thing"} )
jq DOM 节点的扩展
$.fn.extend() 相当于给一个 DOM 元素扩展了一个方法或属性
<body>
<button id="btn">test</button>
</body>
<script src="./jquery-3.7.1.min.js"></script>
<script>
$.fn.extend({
changeColor() {
const $dom = $(this)
$dom.css('background-color', 'red')
setTimeout(() => {
$dom.css('background-color', 'blue')
}, 1000)
return $(this) // 保证链式调用
}
})
$('#btn').click(function (evt) {
$(this).changeColor()
})
</script>
// 拓展一个属性
$.fn.extend({
setProperty(k, v) {
$(this).attr(k, v)
return $(this) // 保证链式调用
}
})
$('#btn').click(function (evt) {
$(this).setProperty("data-btn", "custom")
console.log($(this).attr("data-btn")); // custom
})
示例
原生tab功能
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <title>优化版选项卡</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; list-style: none; } #container { width: 600px; margin: 30px auto; } .option, .content { border: 1px solid #000; text-align: center; font: bold 2rem/5rem Arial; } .option { display: flex; height: 80px; } .option li { flex: 1; cursor: pointer; } .content { height: 300px; border-top: none; } .content li { height: 100%; display: none; } .content li.active { display: block; } .option li.active, .option li:hover { background-color: #000; color: red; } </style> </head> <body> <div id="container"> <ul class="option"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <ul class="content"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> </div> <script src="https://cdn.staticfile.org/jquery/3.7.1/jquery.min.js"></script> <script> $(function () { $.fn.extend({ tabs(defaultIndex) { var $options = $(this).find('.option li'); var $contents = $(this).find('.content li'); // 初始化选中 function setActive(index) { $options.eq(index).addClass('active').siblings().removeClass('active'); $contents.eq(index).addClass('active').siblings().removeClass('active'); } // 默认显示 setActive(defaultIndex || 0); // 点击切换 $options.on('click', function () { setActive($(this).index()); }); } }); // 初始化:默认显示第 2 个(从0开始) $('#container').tabs(2); }); </script> </body> </html>
Accordion 手风琴效果
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
</head>
<body>
<nav class="container">
<div class="title">
<div class="slogon">◷</div>
<p>web class</p>
<i class="down">
<narrow></narrow>
</i>
</div>
<ul class="ul-1">
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
</ul>
<div class="title">
<div class="slogon">◶</div>
<p>web class</p>
<i class="down">
<narrow></narrow>
</i>
</div>
<ul class="ul-2">
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
</ul>
<div class="title">
<div class="slogon">◵</div>
<p>web class</p>
<i class="down">
<narrow></narrow>
</i>
</div>
<ul class="ul-3">
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
</ul>
<div class="title">
<div class="slogon">◴</div>
<p>web class</p>
<i class="down">
<narrow></narrow>
</i>
</div>
<ul class="ul-4">
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
<li>photoshop</li>
</ul>
</nav>
</body>
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script>
//suite mobile
$(document).ready(function () {
$("*").css({ margin: 0, padding: 0 });
$("html").css("font-size", `${$("html")[0].clientWidth / 375 * 100}px`);
//JQuery 实现手风琴效果
$(".container").css({
width: "80vw",
height: "70vh",
border: "1px solid black",
margin: "10vh auto",
overflow: "auto"
})
$(".container>div").css({
display:"table",
width: "100%",
height: "5%",
fontSize: "0.15em",
cursor: "pointer"
}).on("mouseover", function () {
$(this).css("color", "red")
}).on("mouseout", function () {
$(this).css("color", "black")
}).on("click", function () {
$(this).siblings("ul").slideUp(500);
//箭头向上
$(this).siblings("ul").prev().find("narrow").css({
transition:"all 1s 0s ease-in",
transform:"rotate(0deg)",
transformOrigin:"center"
});
//点击的如果是同一元素,需要判断该元素是处于打开还是关闭的状态,否则,切换时将会处于反复打开的问题
if ($(this).next("ul").css("display") == "none") {
$(this).next("ul").slideDown(500);
$(this).css("color", "red")
console.log($(this).children("narrow"))
//箭头向下
$(this).find("narrow").css({
transition:"all 1s 0s ease-in",
transform:"rotate(-180deg)",
transformOrigin:"center"
});
} else {
$(this).next("ul").slideUp(500); //height变化时,display会自动设置成了block,等到变化结束,就会又回到none
//箭头向上
$(this).find("narrow").css({
transition:"all 1s 0s ease-in",
transform:"rotate(0deg)",
transformOrigin:"center"
});
}
});
$(".container .slogon").css({
width:"5%",
height: "100%",
fontSize: "0.5em",
display:"table-cell",
textAlign: "center",
verticalAlign:"middle"
});
$(".container p").css({
width:"90%",
height: "100%",
fontSize: "0.5em",
display:"table-cell",
verticalAlign:"middle"
});
$(".container .down").css({
width:"5%",
height: "100%",
display:"table-cell",
textAlign: "center",
verticalAlign:"middle"
})
$(".container .down narrow").css({
display: "inline-block",
width:"0.05rem",
height: "0.05rem",
backgroundColor:"black",
clipPath:"polygon(50% 0%, 100% 100%, 50% 50%,0 100%)"
})
$(".container>ul").css({
width: "100%",
listStyleType: "none",
textIndent: "0.1rem",
display:"none",
backgroundColor: "black",
color: "white"
})
$(".container li").css({
height: "0.1rem",
lineHeight: "0.1rem",
fontSize: "0.05rem"
}).on("mouseover", function () {
$(this).css({
backgroundColor: "white",
color: "black"
})
}).on("mouseout", function () {
$(this).css({
backgroundColor: "black",
color: "white"
})
})
})
</script>
</html>
购物车页面
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/Mock.js/1.0.0/mock-min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background: #eeeeee
}
input[type="checkbox"] {
appearance: none;
width: 0.8rem;
height: 0.8rem;
background-color: white;
margin: 0 0.5rem;
text-align: center;
line-height: 1rem;
vertical-align: middle
}
input[type="checkbox"]:hover {
background-color: #AF8875;
}
input[type="checkbox"]:checked {
border: 1px solid #FDF7FF;
background-color: #FDF7FF
}
input[type="checkbox"]:checked:after {
content: "✓";
color: purple;
font-weight: bolder;
font-size: 0.8rem;
}
.container {
width: 80%;
height: 100%;
overflow: auto;
margin: auto;
}
.container::-webkit-scrollbar {
width: 3px;
height: 8px;
background: #A73B00;
}
.container::-webkit-scrollbar-thumb {
background: #F9F871;
}
.title {
width: 100%;
height: 4rem;
display: inline-table;
position: sticky;
top: 0;
overflow: hidden;
background: #69bff8;
font-size: 0.8rem;
}
.title span {
display: table-cell;
vertical-align: middle;
text-align: center
}
.title .title-chose {
width: 10%;
text-align: start
}
.title-info {
width: 20%;
}
.product-info {
width: 15%;
padding: 0 2.5%
}
.title-describe {
width: 20%
}
.product-describe {
width: 18%;
padding: 0 2%
}
.title-price,
.product-price,
.title-count,
.product-count,
.title-price-count,
.product-price-count,
.title-remove,
.product-remove {
width: 11%;
font-size: 0.8rem
}
.store-title {
width: 100%;
height: 2rem;
background: #C9F987
}
.store-title label,
.store-title a {
height: 100%;
display: inline-block;
font: bolder 0.8rem/2rem 宋体;
}
.store-title a {
text-decoration: none;
color: #892000;
}
.product-detail {
padding-top: 0.5rem;
font-size: 0;
}
.product-detail li {
height: 15%;
list-style-type: none;
display: inline-block;
vertical-align: middle;
font-size: 0.8rem;
}
.product-img {
width: 10%;
}
.product-img img {
width: 100%;
height: 100%;
}
.product-price,
.product-count,
.product-price-count,
.product-remove {
text-align: center;
}
.product-count input {
vertical-align: top;
}
.product-count input[type="button"] {
appearance: none;
width: 2rem;
height: 1.5rem;
border: 0;
background: #61DBE1;
color: black;
}
.product-count input[type="button"]:hover {
cursor: pointer
}
.product-count input[type="button"]:active {
background: #F9F871;
border: 1px solid #F9F871;
color: white;
}
.product-count input[type="number"] {
outline: none;
width: 3rem;
height: 1.5rem;
text-align: center;
border: 0
}
.product-count input[type="number"]::-webkit-inner-spin-button,
.product-count input[type="number"]::-webkit-outer-spin-button {
appearance: none;
}
.product-price-count {
color: red
}
.product-remove:hover {
color: red;
cursor: pointer
}
.total {
width: 80%;
height: 3rem;
position: absolute;
bottom: 0;
background: white;
overflow: auto;
text-align: right;
}
.total div,
.total button {
height: 100%;
border: 0;
float: left;
color: white;
font: bolder 1.2rem 宋体;
}
.total div {
width: 78%;
background: #BFC1FF;
line-height: 3rem;
padding-right: 2%
}
.total button {
width: 20%;
border: 0;
background: #898CC9;
}
</style>
</head>
<body>
<div class="container">
<section class="title">
<span class="title-chose"><input id="chose-all" type="checkbox" /><label for="chose-all">全选</label></span>
<span class="title-info">商品信息</span>
<span class="title-describe">商品参数</span>
<span class="title-price">单价</span>
<span class="title-count">数量</span>
<span class="title-price-count">金额</span>
<span class="title-remove">操作</span>
</section>
<section class="cart">
<div class="store-title">
<input id="stores" class="store-check" type="checkbox" /><label for="stores" class="store-name">店铺:</label>
<a href="#">null</a>
</div>
<ul class="product-detail">
<li class="product-check"><input type="checkbox" /></li>
<li class="product-img"><img src="" alt="商品图片"></li>
<li class="product-info"><span></span></li>
<li class="product-describe">规格:<span></span><br>尺寸:<span></span></li>
<li class="product-price">$<span></span></li>
<li class="product-count"><input type="button" value="-" /><input type="number" value="1" /><input type="button"
value="+" /></li>
<li class="product-price-count">$<span></span></li>
<li class="product-remove"><span>移除商品</span></li>
<div style="clear:both;"></div>
</ul>
</section>
<section class="total">
<div>总计:<span></span></div>
<button>结算</button>
</section>
</div>
</body>
<script>
// mock data
Mock.mock("http://localhost:3000/shoppingcart", {
"shoppingcart|3-5": [
{
"store": "店铺@integer(1,100)",
"products|1-5": [
{
"name": "商品@integer(1,100)",
"describe-specs": "规格@integer(1,100)",
"describe-size": "尺寸@integer(1,100)",
"price|1-100.2": 1,
"counts|1-10": 1,
"url": "https://picsum.photos/200/300?random=@integer(1,100)"
}
]
}
]
});
//suit for mobile device
$("html").css("font-size", `${$("html").innerWidth / 375 * 12}px`);
//render data to page
function renderData(resultData) {
//link Elements
var cart = $(".cart").attr("data-index", "0");
//add new element shop
for (let j = resultData.length - 1; j >= 0; j--) {
j != 0 ? cart.after(cart.clone().attr("data-index", `${j}`)) : null;
// for(let i = resultData[j].products.length; i > 0; i--) {
// //mistake: (---below---)
// //if there isn't slice() to locate the productDetail , it will add more than expected numbers
// // i != 0 ? productDetail.slice(resultData[j].products.length).after(productDetail.clone()).attr("data",mk) : null;
// }
}
//add new element products in terms of added shop elements
var productDetail = $(".product-detail");
for (let j = resultData.length - 1; j >= 0; j--) {
for (let i = resultData[j].products.length - 1; i >= 0; i--) {
i != 0 ? productDetail.eq(j).after(productDetail.eq(j).clone()) : null;
}
}
//link Elements again after added elements
var cart = $(".cart");
var productDetail = $(".product-detail");
var titleCheck = $(".title-chose input");
var shopCheck = $(".store-check");
var shopCheckLabel = $(".store-check~label");
var shopName = $(".store-title a");
var eachCheck = $(".product-check input");
var img = $(".product-img img");
var info = $(".product-info span");
var describeSpecs = $(".product-describe span:first-of-type");
var describeSize = $(".product-describe span:last-of-type");
var price = $(".product-price span");
var decrease = $(".product-count").children("[type='button']:even");
var increase = $(".product-count").children("[type='button']:odd");
var counts = $(".product-count").children("[type='number']");
var countsPrice = $(".product-price-count span");
var productRemove = $(".product-remove");
var totalPrice = $(".total span");
//get data from ajax and set data-index attribute for event
let m = 0; //index of elements
for (let j = 0; j < resultData.length; j++) {
shopName.eq(j).text(resultData[j].store); //get shop name
shopCheck.eq(j).attr({ "data-index": `${j}`, "id": `${j}` });
shopCheckLabel.eq(j).attr("for", `${j}`);
for (let i = 0; i < resultData[j].products.length; i++) {
if (m < productDetail.length) { //in terms of the productDetail elemnts' numbers
//get data
img.eq(m).attr("src", resultData[j].products[i].url); //get productImg
info.eq(m).text(resultData[j].products[i].name); //get product information
describeSpecs.eq(m).text(resultData[j].products[i]["describe-specs"]);
describeSize.eq(m).text(resultData[j].products[i]["describe-size"]); //get product describe
price.eq(m).text(resultData[j].products[i].price); //get count price
counts.eq(m).val(resultData[j].products[i].counts);
countsPrice.eq(m).text((parseFloat(counts.eq(m).val() * parseFloat(price.eq(m).text()))).toFixed(2));
console.log(parseFloat(price.eq(m).text()))
//set index
eachCheck.eq(m).attr("data-index", `${j}-${i}`); //set check index
price.eq(m).attr("data-index", `${j}-${i}`); //set price index
decrease.eq(m).attr("data-index", `${j}-${i}`);
increase.eq(m).attr("data-index", `${j}-${i}`); //set - + input index
counts.eq(m).attr("data-index", `${j}-${i}`); //set count index
countsPrice.eq(m).attr("data-index", `${j}-${i}`); //set counts' summary price
productRemove.eq(m).attr("data-index", `${j}-${i}`); //set remove operator index
productDetail.eq(m).attr("data-index", `${j}-${i}`); //set each productDetail index
m++;
} // else none
}
}
function countTotalPrice() {
let countsPriceTotal = 0;
let countsPrice = $(".product-price-count span");
for (let i = 0; i < countsPrice.length; i++) {
countsPriceTotal += parseFloat(countsPrice.eq(i).text());
}
totalPrice.text(countsPriceTotal.toFixed(2));
};
countTotalPrice();
return { titleCheck, shopCheck, eachCheck, img, info, describeSpecs, describeSize, price, decrease, increase, counts, countsPrice, productRemove, productDetail, cart, countTotalPrice };
}
//check products
function checkProducts(titleCheck, shopCheck, eachCheck) {
function toggleAll() {
for (let j = 0; j < shopCheck.length; j++) {
if (!shopCheck.eq(j).prop("checked")) {
titleCheck.prop("checked", false);
break; //avoid continuous circle that leads to cover the value
} else if (shopCheck.eq(j).prop("checked")) {
titleCheck.prop("checked", true);
}
}
}
eachCheck.on("click", function () {
var curLineIndex = $(this).attr("data-index").substring(0, 1);
var curEachChecks = eachCheck.filter(`[data-index^='${curLineIndex}']`);
var curLineShop = shopCheck.filter(`[data-index='${curLineIndex}']`);
for (let i = 0; i < curEachChecks.length; i++) {
if (!curEachChecks.eq(i).prop("checked")) {
curLineShop.prop("checked", false);
toggleAll();
break; //avoid continuous circle that leads to cover the value
} else {
curLineShop.prop("checked", true);
toggleAll();
}
}
});
shopCheck.on("click", function () {
var curLineIndex = $(this).attr("data-index");
var curEachChecks = eachCheck.filter(`[data-index^='${curLineIndex}']`);
for (let i = 0; i < curEachChecks.length; i++) {
if ($(this).prop("checked")) {
curEachChecks.eq(i).prop("checked", true);
} else {
curEachChecks.eq(i).prop("checked", false);
}
}
toggleAll();
});
titleCheck.on("click", function () {
if ($(this).prop("checked")) {
eachCheck.prop("checked", true);
shopCheck.prop("checked", true);
} else {
eachCheck.prop("checked", false);
shopCheck.prop("checked", false);
};
});
}
//summary price
function changeNum(price, decrease, increase, counts, countsPrice, productDetail, countTotalPrice) {
decrease.on("click", function () {
for (let i = 0; i < counts.length; i++) {
if ($(this).attr("data-index") === counts.slice(i).attr("data-index")) {
var curCount = counts.filter(`[data-index='${counts.slice(i).attr("data-index")}']`);
var afterCount = parseInt(curCount.val()) - 1;
var curPrice = price.filter(`[data-index='${price.slice(i).attr("data-index")}']`);
var curCountsPrice = countsPrice.filter(`[data-index='${countsPrice.slice(i).attr("data-index")}']`);
if (parseInt(afterCount) !== 0) {
//decrease the input’s number
curCount.val(afterCount);
curCountsPrice.text((parseFloat(curPrice.text()) * afterCount).toFixed(2));
} else {
curCountsPrice.text((parseFloat(curPrice.text()) * parseInt(curCount.val(1).val())).toFixed(2));
}
} //else none
}
});
increase.on("click", function () {
for (let i = 0; i < counts.length; i++) {
if ($(this).attr("data-index") === counts.slice(i).attr("data-index")) {
var curCount = counts.filter(`[data-index='${counts.slice(i).attr("data-index")}']`);
var afterCount = parseInt(curCount.val()) + 1;
var curPrice = price.filter(`[data-index='${price.slice(i).attr("data-index")}']`);
var curCountsPrice = countsPrice.filter(`[data-index='${countsPrice.slice(i).attr("data-index")}']`);
//increase the input’s number
curCount.val(afterCount);
curCountsPrice.text((parseFloat(curPrice.text()) * afterCount).toFixed(2));
} //else none
}
});
counts.on("blur", function () {
for (let i = 0; i < price.length; i++) {
if ($(this).attr("data-index") === price.slice(i).attr("data-index")) {
var curCount = counts.filter(`[data-index='${counts.slice(i).attr("data-index")}']`)
var curPrice = price.filter(`[data-index='${price.slice(i).attr("data-index")}']`);
var curCountsPrice = countsPrice.filter(`[data-index='${countsPrice.slice(i).attr("data-index")}']`);
if (parseInt(curCount.val()) <= 0 || isNaN(parseInt(curCount.val()))) {
curCountsPrice.text(parseInt(curCount.val(1).val()) * parseFloat(curPrice.text()));
} else {
curCount.val(parseInt(curCount.val())); //change producr numbers directly from the input,ensure the value is integer
curCountsPrice.text(parseInt(curCount.val()) * parseFloat(curPrice.text()));
}
}
}
});
productDetail.on("click", function () {
countTotalPrice();
})
}
//operate remove each line product
function eachRemove(productRemove, productDetail, cart, totalPrice) {
productRemove.on("click", function () {
productDetail.filter(`[data-index='${$(this).attr("data-index")}']`).remove();
cart.filter(":not(:has(.product-detail))").remove();
let countsPriceTotal = 0;
let afterRemovePriceCount = $(".product-price-count span");
for (let i = 0; i < afterRemovePriceCount.length; i++) {
countsPriceTotal += parseFloat(afterRemovePriceCount.eq(i).text());
}
$(".total span").text(countsPriceTotal.toFixed(2));
})
}
//ajax function
$.ajax({
url: "http://localhost:3000/shoppingcart",
methos: "get",
data: {},
dataType: "json",
success: function (result, status) {
console.log(result);
let render = renderData(result.shoppingcart);
changeNum(render.price, render.decrease, render.increase, render.counts, render.countsPrice, render.productDetail, render.countTotalPrice)
checkProducts(render.titleCheck, render.shopCheck, render.eachCheck);
eachRemove(render.productRemove, render.productDetail, render.cart, render.totalPrice);
},
error: function (xhr, status, error) {
console.log(status);
console.log(error);
}
});
</script>
</html>