jQuery
jQuery- 01day
1. jQuery的基本使用
-
jQuery : 方法和属性的集合
(1)引入jQuery文件(2)入口函数:等待页面Dom加在完毕在执行js代码
和 DomContentLoad 相似
$(function() {
})
2. jQuery的顶级对象:$
(1)$
是 jQuery 的别称,在代码中可以使用 jQuery 代替 $
(2) $
是$ 是jQuery 的顶级对象, 相当于原生JavaScript中的 window。
3. jQuery对象和Dom对象
3.1 区别
- DOM 对象:用原生 JS 获取来的对象就是
- jQuery 对象: 利用$对DOM 对象包装后产生的对象(
伪数组
形式存储)。 Dom对象的方法多于jQuery对象
<div></div>
<script>
// Dom对象
var mydiv = document.querySelector('div')
// jQuery对象
$('div)
</script>
3.2 互相转换
- Dom对象——> jQuery对象
$(Dom对象)
- jQuery对象——> Dom对象
$('div') [index] index 是索引号
$('div') .get(index) index 是索引号
<div></div>
<script>
// Dom对象
var mydiv = document.querySelector('div')
// jQuery对象
$('div)
// Dom对象——> jQuery对象,注意不用加引号
$(mydiv)
// jQuery对象——> Dom对象
$('div') [0]
$('div') .get(0)
</script>
4. jQuery选择器
4.1 隐式迭代
- 概念:给匹配到的所有元素进行循环遍历,执行相应的方法,而不用我们再进行循环,
4.2 选择器
用法:跟css一样的
- 基础选择器
① $('#id')
② $('*') // 全选选择器
③ $('.class') // 类选择器
④ $('div') // 标签选择器
⑤$('div,p,li') // 并集选择器
⑥$('li.current') // 交集选择器
- 层级选择器
① $('ul>li') // 子代选择器,亲儿子
② $('ul li') // 后代选择器
- 筛选选择器
1. $('li:first') // 获取第一个li 元素
2. $('li:last') // 获取最后一个li元素
3. $('li:eq(2)') // 获取索引号为二的li,index索引号从0开始
4. $('li:odd') // 获取索引号为奇数的元素
5. $('li:even') // 获取索引号为偶数的元素
4.3 jQuery筛选方法(重点)
- 注意:既然是方法,一定要加
()
// 1.查找父级
$('li').parent()
// 2.查找亲儿子
$('.ul').children('li')
// 3.查找子代
$('.ul').find('li')
// 4.查找兄弟元素,不包括本身
$('li').siblings('li)
//5.查找某个元素,相当于$('li:eq(2)'),但是index要是是变量的话,下面更常用
$('li').eq(2)
// 6. 判断是否有某个类; 输出的结果是 true 或者 fase
$('li').hasClass('current')
4.4 排他思想
想要多选一的效果,排他思想:当前元素设置样式,其余的兄弟元素清除样式。
show() 显示元素 ; hide() 隐藏元素 ; index() 获得元素的索引号
(this) Dom对象 ; $(this) jQuery对象
<body>
<button>快速</button>
<button>快速</button>
<button>快速</button>
<button>快速</button>
<button>快速</button>
<button>快速</button>
<button>快速</button>
<script>
$(function () {
// 1.点击某个button,颜色改变,其他兄弟按钮颜色没改变
// 隐式迭代;让所有的button 都绑定点击事件
$('button').click(function() {
$(this).css('color','pink')
// 隐式迭代,让其他兄弟元素依次改颜色
$(this).siblings('button').css('color','')
})
})
</script>
</body>
4.5 链式编程
- 作用:链式编程是为了节省代码量,看起来更优雅。
$(this).css('color', 'red').sibling().css('color', '').children().css('color','blue');
// 解释:
1.当前元素的颜色为红色
2.当前元素的兄弟元素的颜色不改变
3.当前元素的兄弟元素的子集元素颜色为蓝色
5. jQuery样式操作
5.1 操作css样式
- ① 参数只写属性名,则是返回属性值
$(this).css(''color'');
- ② 设置一组样式:参数是属性名,属性值,逗号分隔
注意:属性必须加引号,值如果是数字
可以不用跟单位和引号
\$(this).css(''color'', ''red'');
\$(this).css(''width'', 200)
- ③ 多组样式:对象形式。
注意:属性名和属性值用冒号隔开, 属性可以不用加引号,驼峰命名法
$(this).css({
color:"white",
// 驼峰命名法
backgroundColor: "green",
// 可以不用驼峰命名法,要加引号
"font-size":"20px",
});
5.2 类操作
-
作用:等同于以前的
classList
,可以操作类样式, 注意操作类里面的参数不要加点.
注意:原生 JS 中 className 会覆盖元素原先里面的类名。 jQuery 里面类操作只是对指定类进行操作,不影响原先的类名。
-
① 添加类
$("div").addClass(''current'');
- ② 移除类
$("div").removeClass(''current'');
- ③ 切换类
$("div").toggleClass(''current'');
5.3 案例:tab栏切换
<script>
$(function() {
// 1.点击上面菜单栏中的li,给他添加current类,其他兄弟元素一出current类
$('.tab_list li').click(function() {
$(this).addClass('current').siblings().removeClass('current')
// 2.获取当前li的索引号
var index = $(this).index()
// 3.让对应的内容区的item显示出来,兄弟item隐藏
$('.item').eq(index).show().siblings().hide()
})
})
</script>
6. jQuery动画效果
6.1 显示隐藏
- show():显示;学的动画效果参数都一样,设置透明度是个例
语法规范:show([speed,[easing],[fn]])
// 解释
1.speed:代表速度,就是他运行的时间;表示动画时长的毫秒数值(如:1000)
2.easing:动画圆形的曲线
3.fn:动画执行完毕的回调函数
4.[]:代表可加可不加这个参数
- hide():隐藏
- toggle():切换
6.2 滑动
- slideDown():下滑
- slideUp():上滑
- slideToggle():切换
6.3 淡入淡出
- fadeIn():淡入
- fadeOut():淡出
- fadeToggle():切换
- fadeTo():设定透明度
// 语法规范(透明度)
fadeTo([[speed],opacity,[easing],[fn]])
// 解释:
1.opacity 透明度必须写,取值 0~1 之间。
2.speed:`必须写`,“slow”,“normal”, or “fast”或表示动画时长的毫秒数值(如:1000)。
6.4 自定义动画
- 语法
animate(params,[speed],[easing],[fn])
animate({
key1: value1,
key2: value2,
},[speed],[easing],[fn])
① params: 必须写,想要更改的样式属性,以对象形式传递。
② 属性名可以不用带引号,如果是复合属性则需要采取驼峰命名法
③ 其余参数都可以省略。
6.4.2 案例:手风琴
<script>
$(function () {
// 鼠标经过li的时候
$('.king li').mouseenter(function () {
// 1.当前li的宽度变成224px,li里面的 小图片淡出,大图片淡入
$(this).stop().animate({
width: 224
}).find('.small').stop().fadeOut().siblings('.big').stop().fadeIn()
// 2.兄弟元素的宽度变为69px,小图片淡入,大图片淡出
$(this).siblings().animate({
width: 69
}).find('.small').stop().fadeIn().siblings('.big').stop().fadeOut()
})
})
</script>
6.5 事件切换
hover([over,]out)
.hover(function() {
// mouseenter
}, function() {
// mouseleave
})
(1)over:鼠标移到元素上要触发的函数(相当于mouseenter)
(2)out:鼠标移出元素要触发的函数(相当于mouseleave)
(3)只写一个函数:则鼠标经过和离开都会触发它
6.6 新浪下拉菜单优化
$(function () {
// 鼠标经过nav 下面的li ,下拉框显示
// hover事件,第一个参数是鼠标经过的时候,第二个参数是鼠标一处的时候
/* $('.nav>li').hover(function() {
$(this).children('ul').slideDown()
},function() {
$(this).children('ul').slideUp()
}) */
$('.nav>li').hover(function() {
$(this).children('ul').stop().slideToggle()
})
})
6.6 停止动画排队
stop()
(1)stop() 方法用于停止动画或效果。
(2) 注意: stop() 写到动画或者效果的前面, 相当于停止结束上一次的动画。
$(function () {
// 1.根据全选框的状态,来判断三个小的复选框和另外一个全选框是否选中
// 就是把全选按钮(checkall)的状态赋值给 三个小的按钮(j-checkbox)就可以了
$('.checkall').change(function () {
$('.j-checkbox, .checkall').prop('checked', $(this).prop('checked'))
// 当全选选中的时候,让所有商品添加一个背景色的类,如果没有移除
if($(this).prop('checked')) {
$('.cart-item').addClass('check-cart-item')
} else {
$('.cart-item').removeClass('check-cart-item')
}
})
// 2.给小复选框change事件;如果小复选框的选中个数=3的话,那么全选按钮自动勾上,否则不勾上
$('.j-checkbox').change(function () {
// 就是小复选框的选中个数和小复选框数量相等
/* if ($('.j-checkbox:checked').length == $('.j-checkbox').length) {
$('.checkall').prop('checked',true)
} else {
$('.checkall').prop('checked',false)
} */
// 代码优化
$('.checkall').prop('checked', $('.j-checkbox:checked').length == $('.j-checkbox').length)
// 当小复选框选中的时候,让当前商品添加一个背景色的类,如果没有移除
if($(this).prop('checked')) {
$(this).parents('.cart-item').addClass('check-cart-item')
} else {
$(this).parents('.cart-item').removeClass('check-cart-item')
}
})
// 3.增减商品数量模块
// 点击'+',声明一个变量,让他自增,变量的初始值就是表单里面数量的原先值
$('.increment').click(function () {
var n = $(this).siblings('.itxt').val()
n++
$(this).siblings('.itxt').val(n)
// 4.小计框的计算思路:单价*数量n
// (1)获取单价
var p = $(this).parents('.p-num').siblings('.p-price').text()
p = p.substr(1) // 去除掉¥,方便运算
// (2)单价*数量n
var price = (p * n).toFixed(2)
// (3)把price赋值给小计的文本内容,注意记得加¥
$(this).parents('.p-num').siblings('.p-sum').text('¥' + price)
getSum()
})
// 点击'-',声明一个变量,让他自减,变量的初始值就是表单里面数量的原先值
$('.decrement').click(function () {
var n = $(this).siblings('.itxt').val()
if ($(this).siblings('.itxt').val() == 1) return
n--
$(this).siblings('.itxt').val(n)
// 4.小计框的计算思路:单价*数量n
// (1)获取单价
var p = $(this).parents('.p-num').siblings('.p-price').text()
p = p.substr(1) // 去除掉¥,方便运算
// (2)单价*数量n
var price = (p * n).toFixed(2)
// (3)把price赋值给小计的文本内容,注意记得加¥
$(this).parents('.p-num').siblings('.p-sum').text('¥' + price)
getSum()
})
// 5.用户自己手动输入数量,算出小计总价
// (1)给输入框change事件
$('.itxt').change(function () {
// (2)获取数量
var n = $(this).val()
// (3)获取单价
var p = $(this).parents('.p-num').siblings('.p-price').text()
p = p.substr(1) // 去除掉¥,方便运算
// (4)单价*数量n
var price = (p * n).toFixed(2)
// (5)把price赋值给小计的文本内容,注意记得加¥
$(this).parents('.p-num').siblings('.p-sum').text('¥' + price)
})
// 6.计算总件数和总价钱
// 当前没有发生任何事件的时候,刷新页面,总件数和总价钱自动算好
getSum()
function getSum() {
var count = 0 // 总件数
var money = 0 // 总价钱
$('.itxt').each(function (i, element) { // 遍历
count += parseInt($(element).val()) // 总件数=输入框里面的内容依次相加,要把数据转换成数值型
$('.amount-sum em').html(count) // 再赋值给总件数显示出来
})
$('.p-sum').each(function (i, element) {
money += parseFloat($(element).html().substr(1)) // 把小计中拿来的数据去掉¥,并且转换成数值型
$('.price-sum em').html('¥' + money.toFixed(2))
})
}
// 7.删除商品模块
// (1) 后面的删除小按钮,点击删除当前商品
$('.p-action a').click(function() {
$(this).parents('.cart-item').remove()
getSum()
})
// (2)删除选中商品,小复选框选中的那一行
$('.remove-batch').click(function() {
$('.j-checkbox:checked').parents('.cart-item').remove()
getSum()
})
// (3)清空购物车
$('.clear-all').click(function() {
$('.cart-item').remove()
getSum()
})
})
$(function () {
// 5.节流阀,点击li的时候会添加 current 类,同时会滚动页面;与此同时触发滚动事件,滚动事件里面会给li添加current类
// 所以,会造成混乱这种时候,so我们给个flag判断条件,我们点击li就让 flag = false,动画执行·完毕让 flag = true
var flag = true
// 1.当页面滚动的距离大于推荐服务模块时,固定电梯导航出现
function toggleTool() {
if ($(document).scrollTop() >= $('.recommend').offset().top) {
$('.fixedtool').fadeIn()
} else {
$('.fixedtool').fadeOut()
}
}
// 页面刷新就直接判断页面的位置,来判断显示还是隐藏固定电梯导航
toggleTool()
$(window).scroll(function () {
toggleTool()
// 4.页面滚动到对应位置时,固定电梯导航中对应的li添加current类
// 我们给个flag判断条件
if (flag) {
$('.floor .w').each(function (i,element) {
if ($(document).scrollTop() >= $(element).offset().top) {
$('.fixedtool li').eq(i).addClass('current').siblings().removeClass('current')
}
})
}
})
// 2.点击固定电梯导航相应的选项,跳转到楼层区相应位置
$('.fixedtool li').click(function () {
// 点击li就让 flag = false
flag = false
// 获取当前li的索引号
var index = $(this).index()
// 获取对应索引号距离文档顶部的距离
var currentTop = $('.floor .w').eq(index).offset().top
// 跳转到对应的位置:就是文档滚动的距离 = 对应索引号距离文档顶部的距离
// 用动画效果的话,只能用于dom元素
$('body,html').stop().animate({
scrollTop: currentTop
}, 100,function() {
// 完毕让 flag = true
flag = true
})
// 3.点击时给当前元素添加current类,兄弟元素删除类
$(this).addClass('current').siblings().removeClass('current')
})
// 3.
})
$(function () {
load()
// todolist的值这样子存储 {title: '', done:false}
// 1.输入框按下回车键,把输入框的内容保存到本地存储
$('#title').on('keydown', function (event) {
if (event.keyCode == 13) {
if ($(this).val() == '') {
alert('请输入事项')
} else {
var local = getDate()
// data = JSON.parse(data)
local.push({ title: $(this).val(), done: false })
// 保存本地存储
saveDate(local)
// 渲染页面
load()
$(this).val('')
}
}
})
// 2.删除操作
// 因为a是自己添加的,要用事件委托,但是真正触发点击事件的的是a
$('ol').on('click', 'a', function () {
// a触发的时候让当前索引号的li删除,但是a的索引号无法直接获取,因为不是同一个父元素,这个时候,我们设置一个自定义属性
// 获取索引号
var index = $(this).attr('id')
// 获取本地存储
var data = getDate()
// 修改数据
data.splice(index, 1)
// 保存本地存储
saveDate(data)
// 渲染页面
load()
})
// 3.正在进行完成操作
// 属性done = false 正在进行,done = true 完成;done的值就是复选框的选中状态
$('ol,ul ').on('click', 'input', function () {
// 获取本地存储
var data = getDate()
// 修改数据
var index = $(this).siblings('a').attr('id') // 获取input索引号
data[index].done = $(this).prop('checked')
// 保存本地存储
saveDate(data)
// 渲染页面
load()
})
// 获取本地存储
getDate()
function getDate() {
var data = localStorage.getItem('todolist')
if (data != null) {
return JSON.parse(data)
} else {
return []
}
}
// 保存最新数据
function saveDate(data) {
localStorage.setItem('todolist', JSON.stringify(data))
}
// 渲染页面
function load() {
var data = getDate()
// 遍历之前,先把ol里面的所有内容删除掉
$('ol,ul').empty()
var doneCount = 0
var todoCount = 0
// 遍历data
$.each(data, function (i, ele) {
if (ele.done) {
$("ul").prepend("<li><input type='checkbox' checked='checked' > <p>" + ele.title + "</p> <a href='javascript:;' id=" + i + " ></a></li>");
doneCount++;
} else {
$("ol").prepend("<li><input type='checkbox' > <p>" + ele.title + "</p> <a href='javascript:;' id=" + i + " ></a></li>");
todoCount++;
}
})
$('#todocount').text(todoCount)
$('#donecount').text(doneCount)
}
})
5. 开发属于自己的包
5.1 规范的包结构
-
一个规范的包,它的组成结构,必须符合以下 3 点要求:
- 包必须以单独的目录而存在
- 包的顶级目录下要必须包含
package.json
这个包管理配置文件 package.json
中必须包含name
,version
,main
这三个属性,分别代表包的名字、版本号、包的入口
-
注意:以上 3 点要求是一个规范的包结构必须遵守的格式,关于更多的约束,可以参考这个网址
5.2 了解需要实现的功能
-
需要实现的功能
-
格式化日期
-
转移
HTML
中的特殊字符 -
还原
HTML
中的特殊字符
-
5.3 初始化包的基础结构
-
新建
itheima-tools
文件夹,作为包的根目录 -
在
itheima-tools
文件夹中,新建如下三个文件:package.json
(包管理配置文件)index.js
(包的入口文件)README.md
(包的说明文档)
5.4 初始化 package.json
配置文件
{
"name": "flightloong-tools",
"version": "1.0.0",
"description": "提供格式化时间、HTMLEscape相关功能",
"main": "index.js",
"keywords": [
"itcast",
"itheima",
"dateFormat",
"escape"
],
"license": "ISC"
}
5.5 在 index.js
中定义格式化时间的方法
// 包的入口文件 index.js
// 定义格式化时间的函数
function dateFormat (dateStr) {
const dt = new Date(dateStr)
const y = padZero(dt.getFullYear())
const m = padZero(dt.getMonth() + 1)
const d = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
// 定义一个补零的函数
function padZero (n) {
return n > 9 ? n : '0' + n
}
// 向外暴露需要的成员
module.exports = {
dateFormat
}
// 测试代码
const itheima = require('./flightloong-tools/index')
// 格式化时间的代码
const dtStr = itheima.dateFormat(new Date())
console.log(dtStr) // 2020-06-23 01:16:57
5.6 package.json
文件中 main
的作用
在导入一个文件的时候,如果没有指定一个特定的文件,但是却能够得到某个包的返回内容,这是因为 Node
在使用 require
导入某个路径的时候,发现没有具体的文件,就会看这个路径下查看是否有 package.json
这个文件,如果有,则查看是否有 main
这个属性,如果有,则指定 main
属性对应的文件作为要执行的文件
5.7 在 index.js
中定义转义 HTML 的方法
// index.js
// 定义转义 HTML 字符的函数
function htmlEscape(htmlstr) {
return htmlstr.replace(/<|>|"|&/g, match => {
switch (match) {
case '<':
return '&glt;'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
// test.js
const itheima = require('./flightloong-tools/index')
// 转义 Html 字符串
const htmlStr = '<h4 title="abc">这是h4标签<span>123 </span></h4>'
const str = itheima.htmlEscape(htmlStr)
console.log(str)
5.8 在 index.js
中定义还原 HTML 的方法
// 定义还原 HTML 字符的函数
function htmlUnEscape(str) {
return str.replace(/&glt;|>|"|&/g, (match) => {
switch (match) {
case '&glt;':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
// 还原 Html 字符串
const resetHtml = itheima.htmlUnEscape(str)
console.log(resetHtml)
5.9 划分不同的模块
- 将格式化时间的功能,拆分到
src
->dateFormat.js
中 - 将处理
HTML
字符串的功能,拆分到src
->htmlEscape.js
中 - 在
index.js
中,导入两个模块,得到需要向外共享的方法 - 在
index.js
中,使用module.exports
把对应的方法共享出去
// dateFormat.js
// 定义格式化时间的函数
function dateFormat(dateStr) {
const dt = new Date(dateStr)
const y = padZero(dt.getFullYear())
const m = padZero(dt.getMonth() + 1)
const d = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}
// 定义一个补零的函数
function padZero(n) {
return n > 9 ? n : '0' + n
}
module.exports = {
dateFormat
}
// htmlEscape.js
// 定义转义 HTML 字符的函数
function htmlEscape(htmlstr) {
return htmlstr.replace(/<|>|"|&/g, match => {
switch (match) {
case '<':
return '&glt;'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
// 定义还原 HTML 字符的函数
function htmlUnEscape(str) {
return str.replace(/&glt;|>|"|&/g, (match) => {
switch (match) {
case '&glt;':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
module.exports = {
htmlEscape,
htmlUnEscape
}
// test.js
const itheima = require('./flightloong-tools/index')
// 格式化时间的代码
const dtStr = itheima.dateFormat(new Date())
console.log(dtStr)
// 转义 Html 字符串
const htmlStr = '<h4 title="abc">这是h4标签<span>123 </span></h4>'
const str = itheima.htmlEscape(htmlStr)
console.log(str)
// 还原 Html 字符串
const resetHtml = itheima.htmlUnEscape(str)
console.log(resetHtml)
5.10 编写包的说明文档
- 包根目录中的
README.md
文件,是包的使用说明文档。通过它,我们可以事先把包的使用说明,以markdown
的格式写出来,方便用户参考 README
文件中具体写什么内容,没有强制性的要求;只要能够清晰地把包的作用、用法、注意事项等描述清楚即可- 我们所创建的这个包的
README.md
文档中,会包含以下 6 项内容- 安装方式
- 导入方式
- 格式化时间
- 转义 HTML 中的特殊字符
- 还原 HTML 中的特殊字符
- 开源协议
### 安装
```
npm i flightloong-tools
```
### 导入
```js
const itheima = require('./flightloong-tools')
```
### 格式化时间
```js
// 调用 dateFormat 对时间进行格式化
const dtStr = itheima.dateFormat(new Date())
// 结果 2020-04-03 17:20:58
console.log(dtStr)
```
### 转义 HTML 中的特殊字符
```js
// 带转换的 HTML 字符串
const htmlStr = '<h1 title="abc">这是h1标签<span>123 </span></h1>'
// 调用 htmlEscape 方法进行转换
const str = itheima.htmlEscape(htmlStr)
// 转换的结果 <h1 title="abc">这是h1标签<span>123&nbsp;</span></h1>
console.log(str)
```
### 还原 HTML 中的特殊字符
```js
// 待还原的 HTML 字符串
const str2 = itheima.htmlUnEscape(str)
// 输出的结果 <h1 title="abc">这是h1标签<span>123 </span></h1>
console.log(str2)
```
### 开源协议
ISC
5.11 注册 npm
账号
- 访问 npm 网站,点击
sign up
按钮,进入注册用户界面 - 填写账号相关的信息:
Full Name
、Public Email
、Username
、Password
- 点击
Create an Account
按钮,注册账号 - 登录邮箱,点击验证链接,进行账号的验证
5.12 登录 npm
账号
npm
账号注册完成后,可以在终端中执行npm login
命令,依次输入用户名、密码、邮箱后,即可登录成功- 注意:在运行
npm login
命令之前,必须先把下包的服务器地址切换为npm
的官方服务器。否则会导致发布包失败!
5.13 把包发布到 npm
上
将终端切换到包的根目录之后,运行 npm publish
命令,即可将包发布到 npm
上(注意:包名不能雷同)
5.14 - 删除已发布的包
-
运行
npm unpublish 包名 --force
命令,即可从npm
删除已发布的包 -
注意事项
npm unpublish
命令只能删除 72 小时以内发布的包npm unpublish
删除的包,在 24 小时内不允许重复发布- 发布包的时候要慎重,尽量不要往
npm
上发布没有意义的包!
### .1 购物车案例
0. 功能概述
-
效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qvdh8lwX-1607692736425)(img/购物车效果.png)]
-
分析:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-87KQ3S2r-1607692736428)(img/购物车需求.png)]
1. 实现组件化布局
-
步骤:
- 把静态页面转换成组件化模式
- 把组件渲染到页面上
-
代码:
<div id="app">
<div class="container">
<!-- 2、把组件渲染到页面上 -->
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
# 1、 把静态页面转换成组件化模式
# 1.1 标题组件
var CartTitle = {
template: `
<div class="title">我的商品</div>
`
}
# 1.2 商品列表组件
var CartList = {
# 注意点 : 组件模板必须是单个根元素
template: `
<div>
<div class="item">
<img src="img/a.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/b.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/c.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/d.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
<div class="item">
<img src="img/e.jpg"/>
<div class="name"></div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
<div class="del">×</div>
</div>
</div>
`
}
# 1.3 商品结算组件
var CartTotal = {
template: `
<div class="total">
<span>总价:123</span>
<button>结算</button>
</div>
`
}
## 1.4 定义一个全局组件 my-cart
Vue.component('my-cart',{
## 1.6 引入子组件
template: `
<div class='cart'>
<cart-title></cart-title>
<cart-list></cart-list>
<cart-total></cart-total>
</div>
`,
# 1.5 注册子组件
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
2. 实现标题和结算功能组件
- 步骤:
- 标题组件实现动态渲染
- 从父组件把标题数据传递过来 即 父向子组件传值
- 把传递过来的数据渲染到页面上
- 结算功能组件
- 从父组件把商品列表list 数据传递过来 即 父向子组件传值
- 把传递过来的数据计算最终价格渲染到页面上
- 标题组件实现动态渲染
- 代码:
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
# 2.2 标题组件 子组件通过props形式接收父组件传递过来的uname数据
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
# 2.3 商品结算组件 子组件通过props形式接收父组件传递过来的list数据
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
# 2.4 计算商品的总价 并渲染到页面上
total: function() {
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
# 2.1 父组件向子组件以属性传递的形式 传递数据
# 向 标题组件传递 uname 属性 向 商品结算组件传递 list 属性
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
<cart-list></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
3. 实现列表组件删除功能
- 步骤:
- 从父组件把商品列表list 数据传递过来 即 父向子组件传值
- 把传递过来的数据渲染到页面上
- 点击删除按钮的时候删除对应的数据
- 给按钮添加点击事件把需要删除的id传递过来
- 子组件中不推荐操作父组件的数据有可能多个子组件使用父组件的数据 我们需要把数据传递给父组件让父组件操作数据
- 父组件删除对应的数据
- 给按钮添加点击事件把需要删除的id传递过来
- 代码:
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
# 3.2 把列表数据动态渲染到页面上
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
<input type="text" class="num" />
<a href="">+</a>
</div>
# 3.3 给按钮添加点击事件把需要删除的id传递过来
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
del: function(id){
# 3.4 子组件中不推荐操作父组件的数据有可能多个子组件使用父组件的数据
# 我们需要把数据传递给父组件 让父组件操作数据
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function() {
// 计算商品的总价
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
# 3.1 从父组件把商品列表list 数据传递过来 即 父向子组件传值
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
# 3.5 父组件通过事件绑定 接收子组件传递过来的数据
<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
# 3.6 根据id删除list中对应的数据
delCart: function(id) {
// 1、找到id所对应数据的索引
var index = this.list.findIndex(item=>{
return item.id == id;
});
// 2、根据索引删除对应数据
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>
4. 实现组件更新数据功能 上
- 步骤:
- 将输入框中的默认数据动态渲染出来
- 输入框失去焦点的时候 更改商品的数量
- 子组件中不推荐操作数据 把这些数据传递给父组件 让父组件处理这些数据
- 父组件中接收子组件传递过来的数据并处理
- 代码:
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
<a href="">-</a>
# 1. 将输入框中的默认数据动态渲染出来
# 2. 输入框失去焦点的时候 更改商品的数量 需要将当前商品的id 传递过来
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="">+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
# 3 子组件中不推荐操作数据 因为别的组件可能也引用了这些数据
# 把这些数据传递给父组件 让父组件处理这些数据
this.$emit('change-num', {
id: id,
num: event.target.value
});
},
del: function(id){
// 把id传递给父组件
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function() {
// 计算商品的总价
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
}]
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
# 4 父组件中接收子组件传递过来的数据
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
//4.1 根据子组件传递过来的数据,跟新list中对应的数据
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
// 终止遍历
return true;
}
});
},
delCart: function(id) {
// 根据id删除list中对应的数据
// 1、找到id所对应数据的索引
var index = this.list.findIndex(item=>{
return item.id == id;
});
// 2、根据索引删除对应数据
this.list.splice(index, 1);
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
5. 实现组件更新数据功能 下
- 步骤:
- 子组件通过一个标识符来标记对用的用户点击 + - 或者输入框输入的内容
- 父组件拿到标识符更新对应的组件
- 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.container {
}
.container .cart {
width: 300px;
margin: auto;
}
.container .title {
background-color: lightblue;
height: 40px;
line-height: 40px;
text-align: center;
/*color: #fff;*/
}
.container .total {
background-color: #FFCE46;
height: 50px;
line-height: 50px;
text-align: right;
}
.container .total button {
margin: 0 10px;
background-color: #DC4C40;
height: 35px;
width: 80px;
border: 0;
}
.container .total span {
color: red;
font-weight: bold;
}
.container .item {
height: 55px;
line-height: 55px;
position: relative;
border-top: 1px solid #ADD8E6;
}
.container .item img {
width: 45px;
height: 45px;
margin: 5px;
}
.container .item .name {
position: absolute;
width: 90px;
top: 0;left: 55px;
font-size: 16px;
}
.container .item .change {
width: 100px;
position: absolute;
top: 0;
right: 50px;
}
.container .item .change a {
font-size: 20px;
width: 30px;
text-decoration:none;
background-color: lightgray;
vertical-align: middle;
}
.container .item .change .num {
width: 40px;
height: 25px;
}
.container .item .del {
position: absolute;
top: 0;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script type="text/javascript" src="js/vue.js"></script>
<script type="text/javascript">
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的商品</div>
`
}
var CartList = {
props: ['list'],
template: `
<div>
<div :key='item.id' v-for='item in list' class="item">
<img :src="item.img"/>
<div class="name">{{item.name}}</div>
<div class="change">
# 1. + - 按钮绑定事件
<a href="" @click.prevent='sub(item.id)'>-</a>
<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
<a href="" @click.prevent='add(item.id)'>+</a>
</div>
<div class="del" @click='del(item.id)'>×</div>
</div>
</div>
`,
methods: {
changeNum: function(id, event){
this.$emit('change-num', {
id: id,
type: 'change',
num: event.target.value
});
},
sub: function(id){
# 2 数量的增加和减少通过父组件来计算 每次都是加1 和 减1 不需要传递数量 父组件需要一个类型来判断 是 加一 还是减1 以及是输入框输入的数据 我们通过type 标识符来标记 不同的操作
this.$emit('change-num', {
id: id,
type: 'sub'
});
},
add: function(id){
# 2 数量的增加和减少通过父组件来计算 每次都是加1 和 减1 不需要传递数量 父组件需要一个类型来判断 是 加一 还是减1 以及是输入框输入的数据 我们通过type 标识符来标记 不同的操作
this.$emit('change-num', {
id: id,
type: 'add'
});
},
del: function(id){
// 把id传递给父组件
this.$emit('cart-del', id);
}
}
}
var CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total: function() {
// 计算商品的总价
var t = 0;
this.list.forEach(item => {
t += item.price * item.num;
});
return t;
}
}
}
Vue.component('my-cart',{
data: function() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1,
img: 'img/a.jpg'
},{
id: 2,
name: '机顶盒',
price: 1000,
num: 1,
img: 'img/b.jpg'
},{
id: 3,
name: '海尔冰箱',
price: 1000,
num: 1,
img: 'img/c.jpg'
},{
id: 4,
name: '小米手机',
price: 1000,
num: 1,
img: 'img/d.jpg'
},{
id: 5,
name: 'PPTV电视',
price: 1000,
num: 2,
img: 'img/e.jpg'
}]
}
},
template: `
<div class='cart'>
<cart-title :uname='uname'></cart-title>
# 3 父组件通过事件监听 接收子组件的数据
<cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :list='list'></cart-total>
</div>
`,
components: {
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
#4 分为三种情况:输入框变更、加号变更、减号变更
if(val.type=='change') {
// 根据子组件传递过来的数据,跟新list中对应的数据
this.list.some(item=>{
if(item.id == val.id) {
item.num = val.num;
// 终止遍历
return true;
}
});
}else if(val.type=='sub'){
// 减一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num -= 1;
// 终止遍历
return true;
}
});
}else if(val.type=='add'){
// 加一操作
this.list.some(item=>{
if(item.id == val.id) {
item.num += 1;
// 终止遍历
return true;
}
});
}
}
}
});
var vm = new Vue({
el: '#app',
data: {
}
});
</script>
</body>
</html>