JS DOM中的事件

1、事件模型(DOM事件流) 
1.1 事件:

1.1.1 事件是HTML和Javascript交互的驱动器, 事件是文档或者浏览器窗口中发生的, 特定的交互瞬间。

1.1.2 事件是用户或浏览器自身执行的某种动作, 如 click, load 和 mouseover 都是事件的名字。

1.1.3 事件是 javaScript 和 DOM 之间交互的桥梁。

2、事件流:

2.1 事件流, 即是一个事件发生的流程或者说流转, 从开始到结束, 都发生了什么

2.2 事件发生时会在元素节点之间按照特定的顺序传播, 这个传播过程, 我们就称之为DOM事件流.

2.3 事件流有三个阶段:

// 1. 捕获阶段 Capture Phase; 从上到下, 层层传递, 直到目标接收

// 2. 目标阶段 Target Phase; 确认目标, 进行处理

// 3. 冒泡阶段 Bubbling Phase; 处理结束, 往上传递.

// 4. 先捕获阶段=>目标阶段=>冒泡阶段

3、 注意:

1. 监听绑定方式addEventListener()方法中第三个参数可以控制是在冒泡阶段触发还是在捕获阶段触发

2. JS代码建议执行捕获或者冒泡两个阶段中的其中一个阶段

3. 传统绑定事件方式跟attachEvent绑定事件的方式只能得到"冒泡阶段"

4. addEventListener(type,listener,[,useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序; 如果是false(默认不写就是false),表示事件冒泡阶段调用事件处理程序.

5. 实际开发中我们很少使用事件捕获,我们更关注事件冒泡.

6. 另外需要注意, 不是所有事件都会冒泡的, 有些事件是没有冒泡的,比如onmouseenter,onmouseleave

7. 事件冒泡有时候会带来麻烦,有时候又会帮助我们很巧妙的做某些事件,我们会学习事件委托,事件委托(事件代理)的原理就是事件冒泡

8. 冒泡阶段触发,需要HTML标签是"嵌套关系(父子关系)", 而且需要都绑定事件, 接着,触发子元素事件(子元素事件不触发也可以)的时候, 会逐级向上传播, 把父辈元素相同的事件也会触发

4、 事件对象

4.1 什么是事件对象:

// 事件发生后(JS事件被触发以后),跟事件相关的一系列信息数据的集合都放到一个对象里面,这个对象就是事件对象。

4.2 这个事件对象中会保存什么信息, 比如:

// 1. 谁绑定了这个事件。

// 2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。

// 3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。

4.3 事件对象的使用:

事件触发发生时就会产生事件对象,并且系统会以实参的形式传给事件处理函数。

所以,在事件处理函数中声明1个形参(形参名字可以自定义,但是一般我们会使用event,evt,e这三个名字)用来接收事件对象。

// 传统绑定方式:

dom对象.onclick = function(event){

// 这个event 就是事件对象

};

// 注意: 传统绑定方式,在绑定的时候,没有兼容性问题;但是获取事件对象的时候,有兼容性问题, IE9之间的浏览器需要使用window.event获取事件对象, IE9之间的浏览器无法使用形参的方式获取事件对象

4.4 事件对象的属性和方法:

// 事件对象属性方法 说明

// ❤e.target 返回触发事件的对象 标准

// e.srcElement 返回触发事件的对象 非标准 ie6~8使用

// e.type 返回事件的类型字符串 比如 "click", "mouseover" 是不带on的

// ❤e.stopPropagation() 该方法阻止冒泡 标准

// e.cancelBubble = true 该属性阻止冒泡 非标准 ie6~8使用

// ❤e.preventDefault() 该方法阻止默认事件(默认行为) 标准 比如不让链接跳转

// e.returnValue = false 该属性阻止默认事件(默认行为) 非标准 ie6~8使用 比如不让链接跳转

// 传统绑定方式获取事件对象兼容写法

e = e || window.event;

console.log("兼容获取事件对象e=>", e);

// 传统绑定方式, 兼容性获取事件对象

e = e || window.event;

console.log("e事件对象=>", e);

console.log("e.target=>", e.target);

console.log("e.srcElement=>", e.srcElement);

console.log("e.type=>", e.type);

console.log("");

// 阻止事件冒泡兼容写法

if (e.stopPropagation) {

e.stopPropagation();

} else {

e.cancelBubble = true;

}

// 默认行为的意思就是浏览器对于某些标签本身会有一些行为,比如a标签, 设置了href属性以后,点击a标签会跳转

// 阻止默认行为

if (e.preventDefault) {

// IE9以及IE9以上浏览器阻止默认行为

e.preventDefault();

} else {

// IE9以下浏览器阻止默认行为

e.returnValue = false;

}
5、事件委托:

// 事件委托(事件代理):本身要给子元素绑定的事件, 不给子元素绑定, 而是给父辈元素绑定

// 事件委托的原理: 冒泡

// 事件委托的作用: 可以给动态新增的元素绑定事件

// 给ul绑定鼠标点击事件:

objUl.onclick = function (e) {

console.log("this=>", this);

console.log("e.target=>", e.target);

// nodeName获取节点名称

console.log("e.target.nodeName=>", e.target.nodeName);

// tagName获取标签名称

console.log("e.target.tagName=>", e.target.tagName);

e.target.style.color = "red";

}

6、onchange事件:

注意: option没有设置value的时候,默认会使用option的标签内容作为value值

onchange事件

// 概念: onchange事件当用户更改、和 元素的值并提交这个更改时,onchange 事件在这些元素上触发。和 oninput 事件不一样,onchange 事件并不是每次元素的 value 改变时都会触发。

// 下拉框的onchange需要给select标签绑定

7、oninput事件

oninput 事件在用户输入时触发

该事件在 或 元素的值发生改变时触发。

提示: 该事件类似于 onchange 事件。不同之处在于 oninput 事件在元素值发生变化是立即触发,

oninput跟onchange事件的区别:

1. onchange 在元素失去焦点时触发

2. 在按快捷键ctrl+v粘贴的时候,onkeyup这个事件会触发两次, 但是oninput只触发一次;

3. 鼠标右键粘贴,只能

触发oninput事件以及onchange事件,不能触发onkeyup事件

// 下拉框的oninput需要给select标签绑定

8、事件高级:

8.1 传统注册方式(传统绑定方式) 

1 语法:

// dom对象.on事件类型 = 匿名函数 或者 有名函数名

2 特点:

// 1. 同一个DOM对象绑定同一个类型事件多次, 后面绑定的事件驱动函数会覆盖前面绑定的

// 2. 没有兼容性问题

// 3. 事件驱动函数里面this指向绑定事件的那个DOM对象

8.2 事件监听方式

1 语法:

// dom对象.addEventListener("不带on的事件类型" , 匿名函数 或者 有名函数名 );

2 特点:

// 1. 同一个DOM对象绑定同一个类型事件多次, 会绑定顺序叠加触发事件驱动函数

// 2. 有兼容性问题, IE9以及IE9以后的主流浏览器才可以使用该addEventListener监听方式

// 3. 在事件驱动函数中,this指向绑定这个事件的DOM对象

8.3 IE9之前事件监听方式 

// 语法:

// dom对象.attachEvent("带on的事件类型", 匿名函数 或者 有名函数名 )

// 特点:

// 1. 同一个DOM对象绑定同一个事件多次, 按绑定顺序的倒序叠加执行事件驱动程序

// 2. 有兼容问题,需要低版本的IE浏览器才可以使用该绑定方式(IE 5 6 7 8 9 10)

// 3. 事件驱动程序中this指向window对象

9、删除事件:

9.1 解绑传统绑定的事件语法:

// dom对象.on事件类型 = null;

9.2 解绑addEventListener绑定的事件语法:

// dom对象.removeEventListener("不带on事件类型", 有名函数名 )

9.3 解绑attachEvent绑定的事件语法:

// dom对象.detachEvent("带on事件类型", 有名函数名 )

10、常用键盘事件:

10.1 键盘事件 触发条件

// onkeyup 某个键盘按键被松开时触发

// onkeydown 某个键盘按键被按下时触发

// onkeypress 某个键盘按键被按下时触发 但是它不识别功能键 比如 ctrl shift 箭头等

10.2 注意:

// 1. onkeypress和前面两个onkeyup跟onkeydown的区别是 onkeypress不识别功能键,比如箭头,ctrl,shift 等

// 2. 三个事件的执行顺序是: keydown => keypress => keyup

10.3 键盘事件对象

// 键盘事件对象属性 说明

// e.keyCode 返回该键的ASCII值 e.keyCode只有keypress事件可以区分大小写字母

// e.key 返回用户按下的物理按键的键名

11、常用鼠标事件:

11.1 鼠标事件 触发条件

// click 鼠标点击左键触发

// dblclick 鼠标双击左键触发

// mouseover 鼠标移上触发

// mouseout 鼠标移出触发

// mouseenter 鼠标进入触发

// mouseleave 鼠标离开触发

// mousemove 鼠标移动触发

// mouseup 鼠标弹起触发

// mousedown 鼠标按下触发

// focus 获取鼠标焦点触发

// blur 失去鼠标焦点触发

11.2 鼠标事件对象常用属性:

// event 事件对象是事件相关的一系列信息的集合.

// 现阶段我们主要用鼠标事件对象MouseEvent 和 键盘事件对象KeyBoardEvent

11.3 鼠标事件对象 说明:

// ❤e.clientX 返回鼠标相对于浏览器窗口可视区(不包括滚动条距离)的X坐标

// ❤e.clientY 返回鼠标相对于浏览器窗口可视区(不包括滚动条距离)的Y坐标

// ❤e.pageX 返回鼠标相对于文档页面(包括滚动条距离)的X坐标 IE9+支持

// ❤e.pageY 返回鼠标相对于文档页面(包括滚动条距离)的Y坐标 IE9+支持

// ❤e.offsetX 返回鼠标指针相对于目标节点内边(元素内)位置的X坐标

// ❤e.offsetY 返回鼠标指针相对于目标节点内边(元素内)位置的Y坐标

// e.screenX 返回鼠标相对于电脑屏幕的X坐标

// e.screenY 返回鼠标相对于电脑屏幕的Y坐标

11.4 注意:

// e.pageX/e.pageY 鼠标在页面中的位置 有兼容性问题 从IE9以后才支持

// 扩展:IE无法兼容pageX/pageY解决参考: https://www.cnblogs.com/mushuizzz/p/11847172.html

11.5 mouseenter和mouseover的区别:

// 注意: onmouseover和onmouseout会事件冒泡

// 注意: onmouseenter和onmouseleave不会事件冒泡

12、js三大家族属性:

js三大家族包括offset,client,scroll

三大系列属性,这三个系列属性可以获取宽度高度以及相应的距离

❤❤❤注意: 三大系列属性返回值都是'数值',不带单位, 只能"获取",不能"设置"

12.1 client

// dom对象.clientTop = 上边框的大小

// dom对象.clientLeft = 左边框的大小

// dom对象.clientWidth = width+左padding+右padding

// dom对象.clientHeight = height+上padding+下padding

12.2 offset

// dom对象.offsetWidth 返回自身包括padding、边框、内容区的总宽度 , 返回数值不带单位 其实就是盒子的真实宽度

// dom对象.offsetHeight 返回自身包括padding、边框、内容区的总高度 , 返回数值不带单位 其实就是盒子的真实高度

// dom对象.offsetParent 返回作为该元素带有"非静态定位最近"的父辈元素 如果父辈元素都没有非静态定位则返回"body"元素

// dom对象.offsetLeft 返回元素相对带有"非静态定位最近"的父辈元素左方的偏移量 如果父辈都没有定位则返回相对"body"的"左"方偏移量

// dom对象.offsetTop 返回元素相对带有"非静态定位最近"的父辈元素上方的偏移量 如果父辈都没有定位则返回相对"body"的"上方"偏移量

12.3 scroll

// dom对象.scrollWidth 返回自身实际的宽度,不含边框,返回数值不带单位

// dom对象.scrollHeight 返回自身实际的高度,不含边框,返回数值不带单位

// dom对象.scrollLeft 返回被卷去的左侧距离,返回数值不带单位

// dom对象.scrollTop 返回被卷去的上侧距离,返回数值不带单位

// scroll事件可以给元素绑定,可以给页面绑定 ,当元素滚动的时候,就可以触发scroll事件

// 给页面绑定scroll事件, 可以给document对象, window对象或者body绑定页面滚动事件

❤❤❤一般给window对象绑定

// 注意: 给html对象绑定scroll事件无效

// ❤❤❤window.pageYOffset window.pageXOffset

eg: 封装一个兼容写法 获取页面滚动的距离

function getScroll() {

return {

x: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,

y: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop

  }

}

12.4 补充:

// 滚动窗口至文档中的特定位置:

// 语法一: window.scroll(x-coord,y-coord )

// 语法二: window.scroll(options)

// options 是一个包含三个属性的对象:

// top 等同于 y-coord

// left 等同于 x-coord

// behavior 类型String,表示滚动行为,支持参数 smooth(平滑滚动),instant(瞬间滚动),默认值auto

// 注意:里面的x和y不需要带px单位 直接写数字即可

// 另外,还有一个window.scrollTo函数,实际上和 window.scroll是一样的

// 如果只有一个参数的时候,这个参数需要是一个对象

// window.scroll(水平滚动条位置, 垂直滚动条位置);

// window.scrollTo()的用法跟window.scroll()方法是一样的

// 通过 dom对象.style.css属性名 的方法获取的样式只能是行内式样式属性值 内嵌式和外链式样式都无法获取

// 获取计算以后样式属性值 window.getComputedStyle( element, [pseudoElt] ) 该方法返回一个对象

// 参数说明

// 1. element 用于获取计算样式的Element, 就是DOM对象

// 2. pseudoElt 可选 指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null)

13、延时器和定时器
13.1 延时器:

1 延迟多久以后, "仅执行一次"指定的代码块

// 全局的 setTimeout() 方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码

2 开启延时器语法

// window.setTimeout( function, delay )

// 返回值timeoutID是一个正整数,表示延时器的编号。这个值可以传递给clearTimeout()来取消该延时器。

3 参数说明

// 1. function 是你想要在到期时间 (delay毫秒) 之后执行的函数。

// 2. delay 延迟的毫秒数, 取值数值 ,单位是毫秒 一秒等于 1000 毫秒

4 清除延时器语法

// clearTimeout( timeoutID ) 方法取消了先前通过调用setTimeout()建立的定时器。

// 参数说明: timeoutID 您要取消定时器的标识符。该 ID 由相应的setTimeout()调用返回。

13.2 定时器:

1 每隔指定时间, 就执行一次指定的代码块 不断重复执行

// setInterval() 方法重复调用一个函数或执行一个代码片段,在每次调用之间具有固定的时间间隔。

2 开启定时器语法

// window.setInterval(function, delay);

// 返回值一个 interval ID,该 ID 唯一地标识时间间隔,因此你可以稍后通过调用 clearInterval() 来移除定时器。

3 清除定时器语法

// window.clearInterval(intervalID);

14、window对象

window对象是浏览器的顶级对象,它具有双重角色.

14.1 介绍:

// 1. 它是JS访问浏览器窗口的一个接口.

// 2. 它是一个全局对象. 定义在全局作用域中的变量、函数会变成window对象的属性和方法

// 3. 在调用的时候可以省略window, 比如我们前面学习alert(),prompt()都属于window对象的方法

// 4. 注意:window对象下有一个特殊属性window.name, window.name的属性值是一个字符串

// 5. window.top属性也是window对象中一个内置属性, 它的属性值是指向回window对象自身 top属性值修改不了

14.2 注意:

// 避免使用name和top作为变量名

// name变量名的时候, name的值一定会被改成字符串类型

// top变量名的值会一直指向window对象自身 修改不了

14.3 window对象的属性

// 属性 功能:

// ❤❤❤window.innerWidth 获取浏览器内部的宽度(不包含浏览器的左右两边边框)

// ❤❤❤window.innerHeight 获取浏览器内部的高度(不包含浏览器的菜单栏和地址栏以及上下的边框)

// window.outerWidth 获取整个浏览器的宽度

// window.outerHeight 获取整个浏览器的高度

// 确认框

// window.confirm("提示信息");

14.4 window对象的常见事件

// 1. 页面(窗口)加载事件(两种)

// ❤❤❤1.1 window对象的load事件

// window.onload() 方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。

// window.onload() 通常用于元素,在页面完全载入后(包括DOM结构, 图片、css文件等等)执行脚本代码。

// 1.2 document对象的DOMContentLoaded事件

当初始HTML文档已完全加载和解析时,将触发DOMContentLoaded事件,而不需要等待样式表,图像和子框架页面加载(事件可以用来检测HTML页面是否完全加载完毕(fully-loaded))

// 小结:

// 1. DOMContentLoaded事件绑定的时候, 需要使用事件监听方式绑定

// 2. load事件需要等待HTML结构,CSS样式,图像等都加载完毕以后,再调用load的事件函数

// 3. DOMContentLoaded仅需要等待HTML结构加载完毕即可调用

// 2. 调整窗口大小事件

window对象的resize事件:当浏览器可视区域大小发生变化的时候,就会调用resize事件

// 封装一个获取计算以后样式属性值的方法

function getStyle(dom, attr) {

return window.getComputedStyle(dom)[attr];

}

// 版本一:

封装缓动框架(任意一个数值属性)

* 封装元素js缓慢动画框架

* 参数1 {object} dom 要做动画的dom对象

* 参数2 {string} attr 要做动画的CSS属性名

* 参数3 {number} target 要做动画的CSS属性名的目标值

function animate(dom, attr, target) {

// 清除之前的定时器

clearInterval(dom.intervalId);

// 开启定时器

dom.intervalId = setInterval(function () {

// 获取当前dom的attr对应的属性值

var curVal = parseFloat(getStyle(dom, attr));

// 判断是否达到目标值target

if (curVal === target) {

// 清除定时器

clearInterval(dom.intervalId);

// 使用return关键字,终止函数体的执行

return;

}

// 缓慢动画的意思就是速度由快变慢, 运动速度越来越慢

// 缓慢动画公式: 速度 = ( 目标值 - 当前值 ) / 10;

var speed = (target - curVal) / 10;

// 因为速度有可能得到小数, 小数会导致我们无法达到目标值, 所以需要对速度进行取整

speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

// 设置dom对象对应的attr属性值

dom.style[attr] = curVal + speed + "px";

}, 15);

}

// 给按钮绑定鼠标点击事件

btns[0].onclick = function () {

animate(objDiv, "width", 500);

btns[1].onclick = function () {

animate(objP, "left", 50);

}

// 版本二:

封装缓动框架(多个数值属性)

* 封装元素js缓慢动画框架

* 参数1 {object} dom 要做动画的dom对象

* 参数2 {object} obj 要做动画的一对或者多对CSS属性名和目标值 组成的对象

function animate(dom, obj) {

// 清除之前的定时器

clearInterval(dom.intervalId);

// 开启定时器

dom.intervalId = setInterval(function () {

// for...in遍历obj对象 attr就是css属性名

for (var attr in obj) {

// 获取目标值

var target = obj[attr];

// 获取当前dom的attr对应的属性值

var curVal = parseFloat(getStyle(dom, attr));

// 判断是否达到目标值target

if (curVal === target) {

// 清除定时器

clearInterval(dom.intervalId);

// 使用return关键字,终止函数体的执行

return;

}

var speed = (target - curVal) / 10;

speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

// 设置dom对象对应的attr属性值

dom.style[attr] = curVal + speed + "px";

}

}, 15);

}

// 给按钮绑定鼠标点击事件

btns[0].onclick = function () {

animate(objDiv, {

"width": 500,

"height": 300

});

}

btns[1].onclick = function () {

animate(objP, {

"height": 300,

});

}

或者

*/

function animate(dom, obj) {

// 清除之前的定时器

clearInterval(dom.intervalId);

// 开启定时器

dom.intervalId = setInterval(function () {

// 在定时器内部定义一个变量, 保存是否所有CSS属性都达到了目标值

var flag = true; // 假设所有属性已经达到了目标

// for...in遍历obj对象 attr就是css属性名

for (var attr in obj) {

// 获取目标值

var target = obj[attr];

// 获取当前dom的attr对应的属性值

var curVal = parseFloat(getStyle(dom, attr));

// 判断是否达到目标值

if (curVal != target) { // 如果没有达到

// 修改flag变量的值

flag = false;

}

var speed = (target - curVal) / 10;

speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

// 设置dom对象对应的attr属性值

dom.style[attr] = curVal + speed + "px";

}

// for...in结束,就表示所有属性都遍历完毕了

if (flag) { // 判断flag的值

// 清除定时器

clearInterval(dom.intervalId);

}

}, 15);

}

// 给按钮绑定鼠标点击事件

btns[0].onclick = function () {

animate(objDiv, {

"width": 500,

"height": 300,

"font-size": 50

});

}

// 给按钮绑定鼠标点击事件

btns[1].onclick = function () {

animate(objP, {

"height": 300,

});

}

// 版本三: 封装缓动框架(添加回调函数)

* 封装元素js缓慢动画框架

* 参数1 {object} dom 要做动画的dom对象

* 参数2 {object} obj 要做动画的一对或者多对CSS属性名和目标值 组成的对象

* 参数3 可选参数 {function} callback 动画完成以后,要调用的函数 回调函数就是满足一定条件才调用的函数

function animate(dom, obj, callback) {

// 清除之前的定时器

clearInterval(dom.intervalId);

// 开启定时器

dom.intervalId = setInterval(function () {

// 在定时器内部定义一个变量, 保存是否所有CSS属性都达到了目标值

var flag = true; // 假设所有属性已经达到了目标

// for...in遍历obj对象 attr就是css属性名

for (var attr in obj) {

// opacity属性特殊处理

if (attr == "opacity") {

// 获取目标值

var target = obj[attr] * 100;

// 获取当前dom的attr对应的属性值

var curVal = parseFloat(getStyle(dom, attr)) * 100;

// 判断是否达到目标值

if (curVal != target) { // 如果没有达到

// 修改flag变量的值

flag = false;

}

// 缓慢动画的意思就是速度由快变慢, 运动速度越来越慢

// 缓慢动画公式: 速度 = ( 目标值 - 当前值 ) / 10;

var speed = (target - curVal) / 10;

// 因为速度有可能得到小数, 小数会导致我们无法达到目标值, 所以需要对速度进行取整

speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

// 设置dom对象对应的attr属性值

dom.style[attr] = (curVal + speed) / 100;

} else if (attr == "z-index" || attr == "zIndex") {

// z-index或者zIndex属性特殊处理

dom.style[attr] = obj[attr];

} else {

// 获取目标值

var target = obj[attr];

// 获取当前dom的attr对应的属性值

var curVal = parseFloat(getStyle(dom, attr));

// 判断是否达到目标值

if (curVal != target) { // 如果没有达到

// 修改flag变量的值

flag = false;

}

// 缓慢动画的意思就是速度由快变慢, 运动速度越来越慢

// 缓慢动画公式: 速度 = ( 目标值 - 当前值 ) / 10;

var speed = (target - curVal) / 10;

// 因为速度有可能得到小数, 小数会导致我们无法达到目标值, 所以需要对速度进行取整

speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

// 设置dom对象对应的attr属性值

dom.style[attr] = curVal + speed + "px";

}

}

// for...in结束,就表示所有属性都遍历完毕了

if (flag) { // 判断flag的值

// 清除定时器

clearInterval(dom.intervalId);

// 动画完成了,所以我们可以调用回调函数, 前提是存在回调函数

if (callback !== undefined && typeof callback === "function") {

callback();

}

}

}, 15);

}

// 给按钮绑定鼠标点击事件

btns[0].onclick = function () {

animate(objDiv, {

"width": 500

}, function () {

animate(objDiv, {

"height": 300

}, function () {

animate(objDiv, {

"font-size": 50

});

})

});

}

// 给按钮绑定鼠标点击事件

btns[1].onclick = function () {

animate(objP, {

"height": 300,

});

}
  • 29
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨桃贝贝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值