js高级程序设计第六部分

js高级程序设计:事件、表单操作

事件

事件就是用户或浏览器自身执行的某种动作。可以使用侦听器(或处理程序)来预订事件,以便事件发生时执行相应的代码

事件流,从页面中接收事件的顺序

事件冒泡

事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)

事件捕获

事件捕获的思想是不太具体的节点应该更早接收到事件,而最具体的节点应该最后接收到事件。事件捕获的用意在于在事件到达预定目标之前捕获它
后面的内容在首页不显示,请点击下方的展开全文或者

DOM事件流
  1. “DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段
  2. 首先发生的是事件捕获,为截获事件提供了机会。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAvSaz8R-1569921349913)(JavaScript高级程序设计_files/18.jpg)]
  3. 即使“DOM2 级事件”规范明确要求捕获阶段不会涉及事件目标,但大部分高级浏览器都会在捕获阶段触发事件对象上的事件。结果,就是有两个机会在目标对象上面操作事件。

事件处理程序

响应某个事件的函数就叫做事件处理程序或事件侦听器,一般事件处理程序的名字以"on"开头,如onclick,onload等

HTML事件处理程序,不常用

写在HTML代码里的

<input type="button" value="Click Me" onclick="alert('Clicked')" />
<input type="button" value="Click Me" onclick="showMessage()" />
<input type="button" value="Click Me" onclick="alert(this.value)">
  1. 特别之处:这样会创建一个封装着元素属性值的函数。这个函数中有一个局部变量 event,也就是事件对象,通过 event 变量,可以直接访问事件对象,并且在这个函数内部,this 值等于事件的目标元素
  2. 缺点:
  • 存在一个时差问题。
    • 因为用户可能会在HTML 元素一出现在页面上就触发相应的事件,但当时的事件处理程序有可能尚不具备执行条件
    • 解决:很多 HTML 事件处理程序都会被封装在一个 try-catch 块中,以便错误不会浮出水面
<input type="button" value="Click Me" onclick="try{showMessage();}catch(ex){}">
  • 这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果
  • HTML 与 JavaScript 代码紧密耦合
DOM0 级事件处理程序

在js代码域里写,把函数赋值给一个事件

var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
 alert("Clicked"); 
};
  1. 使用 DOM0 级方法指定的事件处理程序被认为是元素的方法。因此,这时候的事件处理程序是在元素的作用域中运行;换句话说,程序中的 this 引用当前元素
  2. 以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理
  3. 删除通过 DOM0 级方法指定的事件处理程序
btn.onclick = null; //删除事件处理程序
DOM2 级事件处理程序

使用addEventListener()和 removeEventListener()

var btn = document.getElementById("myBtn"); 
btn.addEventListener("click", function(){ //注意不是onclick
 alert(this.id); 
}, false);
  1. 所有 DOM 节点中都包含这两个方法,并且它们都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。
  2. 使用 DOM2 级方法添加事件处理程序的主要好处是可以添加多个事件处理程序
  3. 通过 addEventListener()添加的事件处理程序只能使用 removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同
  4. 注意:通过 addEventListener()添加的匿名函数将无法移除,因为匿名函数无法存在相同的函数
IE事件处理程序

attachEvent()和 detachEvent()

var btn = document.getElementById("myBtn"); 
btn.attachEvent("onclick", function(){ 
 alert("Clicked"); 
});
  1. 这两个方法接受相同的两个参数:事件处理程序名称与事件处理程序函数,
  2. 通过attachEvent()添加的事件处理程序都会被添加到冒泡阶段
  3. 与DOM0级方法的区别:事件处理程序的作用域不同
  • DOM0 级方法的情况下,事件处理程序会在其所属元素的作用域内运行
  • attachEvent()方法的情况下,事件处理程序会在全局作用域中运行,因此 this 等于 window。
  1. 使用attachEvent(),为同一个按钮添加了多个不同的事件处理程序,程序以相反的顺序被触发。
  2. 使用 attachEvent()添加的事件可以通过 detachEvent()来移除,条件是必须提供相同的参数。与 DOM 方法一样,这也意味着添加的匿名函数将不能被移除
跨浏览器的事件处理程序
  1. 要保证处理事件的代码能在大多数浏览器下一致地运行,只需关注冒泡阶段
  2. addHandler(),它的职责是视情况分别使用 DOM0 级方法、DOM2 级方法或 IE 方法来添加事件。这个方法属于一个名叫 EventUtil 的对象,使用这个对象来处理浏览器间的差异。addHandler()方法接受 3 个参数:要操作的元素、事件名称和事件处理程序函数。
  3. removeHandler(),接受与addHandler()相同的参数。这个方法的职责是移除之前添加的事件处理程序——无论该事件处理程序是采取什么方式添加到元素中的,如果其他方法无效,默认采用 DOM0 级方法
var EventUtil = { 
 addHandler: function(element, type, handler){ 
 if (element.addEventListener){ 
 element.addEventListener(type, handler, false); 
 } else if (element.attachEvent){ 
 element.attachEvent("on" + type, handler); 
 } else { 
 element["on" + type] = handler; 
 } 
 }, 
 removeHandler: function(element, type, handler){ 
 if (element.removeEventListener){ 
 element.removeEventListener(type, handler, false); 
 } else if (element.detachEvent){ 
 element.detachEvent("on" + type, handler); 
 } else { 
 element["on" + type] = null; 
 } 
 } 
};

var btn = document.getElementById("myBtn"); 
var handler = function(){ 
 alert("Clicked"); 
}; 
EventUtil.addHandler(btn, "click", handler);

事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。只有在事件处理程序执行期间,event 对象才会存在

DOM中的事件对象
  1. 兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中,无论是用DOM0 级或 DOM2 级
var btn = document.getElementById("myBtn"); 
btn.onclick = function(event){ 
 alert(event.type); //"click" 
}; 
btn.addEventListener("click", function(event){ 
 alert(event.type); //"click" 
}, false);
  1. event 对象属性[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Ew1IrJb-1569921349915)(JavaScript高级程序设计_files/19.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMP3tkAW-1569921349916)(JavaScript高级程序设计_files/20.jpg)]
  2. 在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则 this、currentTarget 和 target 包含相同的值。
var btn = document.getElementById("myBtn"); 
btn.onclick = function(event){ 
 alert(event.currentTarget === this); //true 
 alert(event.target === this); //true 
};
  1. 在需要通过一个函数处理多个事件时,可以使用 type 属性
var btn = document.getElementById("myBtn"); 
var handler = function(event){
switch(event.type){ 
 case "click": 
 alert("Clicked"); 
 break; 
 case "mouseover": 
 event.target.style.backgroundColor = "red"; 
 break; 
 case "mouseout": 
 event.target.style.backgroundColor = ""; 
 break; 
 } 
}; 
btn.onclick = handler; 
btn.onmouseover = handler; 
btn.onmouseout = handler;
  1. 要阻止特定事件的默认行为,可以使用 preventDefault()方法,注意只有 cancelable 属性设置为 true 的事件,才可以使用 preventDefault()来取消其默认行为
//阻止链接导航
var link = document.getElementById("myLink"); 
link.onclick = function(event){ 
 event.preventDefault(); 
};
  1. stopPropagation()方法用于立即停止事件在 DOM 层次中的传播,即取消进一步的事件捕获或冒泡
//避免触发注册在 document.body 上面的事件处理程序
var btn = document.getElementById("myBtn"); 
btn.onclick = function(event){ 
 alert("Clicked"); 
 event.stopPropagation(); 
}; 
document.body.onclick = function(event){ 
 alert("Body clicked"); 
};
  1. eventPhase 属性,可以用来确定事件当前正位于事件流的哪个阶段。如果是在捕获阶段调用的事件处理程序,那么 eventPhase 等于 1;如果事件处理程序处于目标对象上,则 eventPhase 等于 2;如果是在冒泡阶段调用的事件处理程序,eventPhase 等于 3
  2. 在HTML里面指定的事件处理程序,那么还可以通过一个名叫event的变量来访问event对象
<input type="button" value="Click Me" onclick="alert(event.type)">
IE中的事件对象
  1. 在使用 DOM0 级方法添加事件处理程序时,event 对象作为 window 对象的一个属性存在
var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
 var event = window.event; 
 alert(event.type); //"click" 
};
  1. 使用 attachEvent()添加的,那么就会有一个 event 对象作为参数被传入事件处理程序函数中
  2. 在HTML里面指定的事件处理程序,那么还可以通过一个名叫event的变量来访问event对象
  3. IE 的 event 对象的属性或方法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5mR9YCk4-1569921349919)(JavaScript高级程序设计_files/21.jpg)]
  4. 因为事件处理程序的作用域是根据指定它的方式来确定的,所以不能认为 this 会始终等于事件目标。故而,最好还是使用 event.srcElement 比较保险
var btn = document.getElementById("myBtn"); 
btn.onclick = function(){ 
 alert(window.event.srcElement === this); //true 
}; 
btn.attachEvent("onclick", function(event){ 
 alert(event.srcElement === this); //false 
});
  1. returnValue 属性相当于 DOM 中的 preventDefault()方法,它们的作用都是取消给定事件的默认行为。只要将 returnValue 设置为 false,就可以阻止默认行为
  2. ,cancelBubble 属性是用来停止事件冒泡的(IE 不支持事件捕获)
跨浏览器的事件对象,P378

事件类型

UI事件
  1. UI 事件指的是那些不一定与用户操作有关的事件
  • load:当页面完全加载后在 window 上面触发,当所有框架都加载完毕时在框架集上面触发,当图像加载完毕时在元素上面触发,或者当嵌入的内容加载完毕时在元素上面触发。
  • unload:当页面完全卸载后在 window 上面触发,当所有框架都卸载后在框架集上面触发,或者当嵌入的内容卸载完毕后在元素上面触发。
  • abort:在用户停止下载过程时,如果嵌入的内容没有加载完,则在元素上面触发。
  • error:当发生 JavaScript 错误时在 window 上面触发,当无法加载图像时在元素上面触发,当无法加载嵌入内容时在元素上面触发,或者当有一或多个框架无法加载时在框架集上面触发
  • select:当用户选择文本框(或)中的一或多个字符时触发
  • resize:当窗口或框架的大小变化时在 window 或框架上面触发
  • scroll:当用户滚动带滚动条的元素中的内容时,在该元素上面触发。元素中包含所加载页面的滚动条。
  1. 这些事件在 DOM2 级事件中都归为 HTML 事件,确定浏览器是否支持
//要确定浏览器是否支持 DOM2 级事件规定的 HTML 事件,true为支持
var isSupported = document.implementation.hasFeature("HTMLEvents", "2.0");
//确定浏览器是否支持“DOM3 级事件”定义的事件
var isSupported = document.implementation.hasFeature("UIEvent", "3.0");
  1. load事件,当页面完全加载后(包括所有图像、JavaScript 文件、CSS 文件等外部资源),就会触发 window 上面的 load 事件
  • 定义 onload 事件处理程序的方式:
    • 使用js代码,推荐这个
//EventUtil在前面定义的可以跨浏览器的对象
EventUtil.addHandler(window, "load", function(event){ 
 alert("Loaded!"); 
});
+ 为<body>元素添加一个 onload 特性
  • 图像上面也可以触发 load 事件
  • 在高版本浏览器中,script元素也会触发 load 事件,以便开发人员确定动态加载的 JavaScript 文件是否加载完毕,只有在设置了script元素的 src 属性并将该元素添加到文档后,才会开始下载 JavaScript 文件
  1. unload 事件在文档被完全卸载后(用户从一个页面切换到另一个页面)触发,
  • 用处:清除引用,以避免内存泄漏
  • 定义方式
    • 用js代码
//此时生成的 event 对象在兼容 DOM 的浏览器中只包含 target 属性(值为 document)
EventUtil.addHandler(window, "unload", function(event){ 
 alert("Unloaded"); 
});
+ <body>元素添加一个特性
  1. resize 事件
  • 当浏览器窗口被调整到一个新的高度或宽度时,就会触发 resize 事件,这个事件在 window(窗口)上面触发,因此可以通过 JavaScript 或者元素中的 onresize 特性来指定事件处理程序
EventUtil.addHandler(window, "resize", function(event){ 
 alert("Resized"); 
});
  1. scroll 事件
  • 在混杂模式下,可以通过元素的 scrollLeft 和 scrollTop 来监控到这一变化;
  • 在标准模式下,除 Safari 之外的所有浏览器都会通过元素来反映这一变化(Safari 仍然基于跟踪滚动位置
焦点事件,不冒泡
  1. 焦点事件会在页面元素获得或失去焦点时触发,利用这些事件并与 document.hasFocus()方法及document.activeElement 属性配合,可以知晓用户在页面上的行踪
  2. 有4个焦点事件
  • blur:在元素失去焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
  • focus:在元素获得焦点时触发。这个事件不会冒泡;所有浏览器都支持它。
  • focusin:在元素获得焦点时触发。这个事件与 HTML 事件 focus 等价,但它冒泡。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。
  • focusout:在元素失去焦点时触发。这个事件是 HTML 事件 blur 的通用版本。支持这个事件的浏览器有 IE5.5+、Safari 5.1+、Opera 11.5+和 Chrome。
  1. 当焦点从页面中的一个元素移动到另一个元素,会依次触发下列事件:
  • focusout 在失去焦点的元素上触发;
  • focusin 在获得焦点的元素上触发;
  • blur 在失去焦点的元素上触发;
  • focus 在获得焦点的元素上触发;
  1. 要确定浏览器是否支持这些事件,可以使用如下代码:
var isSupported = document.implementation.hasFeature("FocusEvent", "3.0");
鼠标与滚轮事件
  1. 主要的鼠标事件
  • click:在用户单击主鼠标按钮(一般是左边的按钮)或者按下回车键时触发。这一点对确保易访问性很重要,意味着 onclick 事件处理程序既可以通过键盘也可以通过鼠标执行。
  • dblclick:在用户双击主鼠标按钮(一般是左边的按钮)时触发
  • mousedown:在用户按下了任意鼠标按钮时触发。
  • mouseenter:在鼠标光标从元素外部首次移动到元素范围之内时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发
  • mouseleave:在位于元素上方的鼠标光标移动到元素范围之外时触发。这个事件不冒泡,而且在光标移动到后代元素上不会触发
  • mousemove:当鼠标指针在元素内部移动时重复地触发。
  • mouseout:在鼠标指针位于一个元素上方,然后用户将其移入另一个元素时在原元素上触发
  • mouseover:在鼠标指针位于一个元素外部,然后用户将其首次移入另一个元素边界之内时在新元素上触发。
  • mouseup:在用户释放鼠标按钮时触发。不能通过键盘触发这个事件
  1. 事件关系
  • 只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件
  • 只有触发两次 click 事件,才会触发一次 dblclick 事件
  • 一次dblclick 事件触发的顺序:mousedown–mouseup–click–mousedown–mouseup–click–dbclick
  1. 检测浏览器是否支持
//DOM2级
var isSupported = document.implementation.hasFeature("MouseEvents", "2.0");
//DOM3级
var isSupported = document.implementation.hasFeature("MouseEvent", "3.0")
  1. 客户区坐标位置
    鼠标事件发生时鼠标指针在视口中的水平和垂直坐标为事件对象的 clientX 和clientY 属性
  2. 页面坐标位置
    页面坐标通过事件对象的 pageX 和pageY 属性,能告诉你事件是在页面中的什么位置发生的
    在页面没有滚动的情况下,pageX 和 pageY 的值与 clientX 和 clientY 的值相等。
  3. 屏幕坐标位置
    通过 screenX 和 screenY 属性就可以确定鼠标事件发生时鼠标指针相对于整个屏幕的坐标信息
  4. 修改键
    Shift、Ctrl、Alt 和 Meta(在 Windows 键盘中是 Windows 键,在苹果机中是 Cmd 键)被用来修改鼠标事件的行为
  5. 相关元素
  • 对 mouseover 事件而言,事件的主目标是获得光标的元素,而相关元素就是那个失去光标的元素。
  • 对 mouseout 事件而言,事件的主目标是失去光标的元素,而相关元素则是获得光标的元素
  • DOM 通过 event 对象的 relatedTarget 属性提供了相关元素的信息。这个属性只对于 mouseover和mouseout事件才包含值;对于其他事件,这个属性的值是null
  1. 鼠标按钮
  • 对于 mousedown 和 mouseup 事件来说,则在其 event 对象存在一个 button 属性,表示按下或释放的按钮。
  • DOM 的 button 属性可能有如下 3 个值:0 表示主鼠标按钮,1 表示中间的鼠标按钮(鼠标滚轮按钮),2 表示次鼠标按钮
  1. 更多事件信息
  • event 对象中还提供了 detail 属性,用于给出有关事件的更多信息
  • 对于鼠标事件来说,detail 中包含了一个数值,表示在给定位置上发生了多少次单击,鼠标移动后数字清零
  1. 鼠标滚轮事件
    当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时(无论向上还是向下),就会触发 mousewheel
    事件。这个事件可以在任何元素上面触发,最终会冒泡到 document(IE8)或 window(IE9、Opera、Chrome 及 Safari)对象
  2. 触摸设备,移动端
    注意几点:
  • 不支持 dblclick 事件。双击浏览器窗口会放大画面,而且没有办法改变该行为。
  • 轻击可单击元素会触发 mousemove 事件。如果此操作会导致内容变化,将不再有其他事件发生;如果屏幕没有因此变化,那么会依次发生 mousedown、mouseup 和 click 事件。轻击不可单击的元素不会触发任何事件。可单击的元素是指那些单击可产生默认操作的元素(如链接),或者那些已经被指定了 onclick 事件处理程序的元素。
  • mousemove 事件也会触发 mouseover 和 mouseout 事件。
  • 两个手指放在屏幕上且页面随手指移动而滚动时会触发 mousewheel 和 scroll 事件。
  1. 无障碍性问题,使用屏幕阅读器的
  • 使用 click 事件执行代码。有人指出通过 onmousedown 执行代码会让人觉得速度更快,对视力正常的人来说这是没错的。但是,在屏幕阅读器中,由于无法触发 mousedown 事件,结果就会造成代码无法执行。
  • 不要使用 onmouseover 向用户显示新的选项。原因同上,屏幕阅读器无法触发这个事件。如果确实非要通过这种方式来显示新选项,可以考虑添加显示相同信息的键盘快捷方式。
  • 不要使用 dblclick 执行重要的操作。键盘无法触发这个事件。
键盘与文本事件
  1. 3个键盘事件
  • keydown:当用户按下键盘上的任意键时触发,而且如果按住不放的话,会重复触发此事件。
  • keypress:当用户按下键盘上的字符键时触发,而且如果按住不放的话,会重复触发此事件。按下 Esc 键也会触发这个事件。Safari 3.1 之前的版本也会在用户按下非字符键时触发 keypress事件。
  • keyup:当用户释放键盘上的键时触发
  • 触发顺序1:在用户按了一下键盘上的字符键时,首先会触发 keydown 事件,然后紧跟着是 keypress 事件,最后会触发 keyup 事件。
    • 其中,keydown 和 keypress 都是在文本框发生变化之前被触发的;
    • 而 keyup事件则是在文本框已经发生变化之后被触发的。
    • 如果用户按下了一个字符键不放,就会重复触发keydown 和 keypress 事件,直到用户松开该键为止。
  • 触发顺序2:如果用户按下的是一个非字符键,那么首先会触发 keydown 事件,然后就是 keyup 事件。
    • 如果按住这个非字符键不放,那么就会一直重复触发 keydown 事件,直到用户松开这个键,此时会触发 keyup事件。
  1. 1个文本事件:textInput,在文本插入文本框之前会触发 textInput 事件
  2. 键码
  • 在发生 keydown 和 keyup 事件时,event 对象的 keyCode 属性中会包含一个代码,与键盘上一个特定的键对应
  • 键码表[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NbvEecbB-1569921349920)(JavaScript高级程序设计_files/22.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwMmyWDN-1569921349922)(JavaScript高级程序设计_files/23.jpg)]
  • 字符编码–对应字符的ASCII 编码
  1. DOM3变化
  • DOM3级事件中的键盘事件,包含两个新属性:key 和 char。兼容性不好,不推荐
  1. textInput 事件,当用户在可编辑区域中输入实际字符时,就会触发这个事件,它的event 对象中还包含一个 data 属性,这个属性的值就是用户输入的字符(而非字符编码)。
复合事件

复合事件,用于处理 IME 的输入序列。IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符

变动事件
HTML5事件
  1. contextmenu 事件表示何时应该显示上下文菜单,以便开发人员取消默认的上下文菜单而提供自定义的菜单
  2. beforeunload 事件,会在浏览器卸载页面之前触发,可以通过它来取消卸载并继续使用原有页面。这个事件的意图是将控制权交给用户。显示的消息会告知用户页面行将被卸载(正因为如此才会显示这个消息),询问用户是否真的要关闭页面,还是希望继续留下来
  3. DOMContentLoaded 事件在形成完整的 DOM 树之后就会触发,不理会图像、JavaScript 文件、CSS 文件或其他资源是否已经下载完毕
  4. readystatechange 事件,提供与文档或元素的加载状态有关的信息
  5. pageshow 和 pagehide 事件
  • Firefox 和 Opera 有一个特性,名叫“往返缓存”(back-forward cache,或 bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存着页面数据,还保存了 DOM 和 JavaScript 的状态
  • pageshow这个事件在页面显示时触发,无论该页面是否来自 bfcache
  • 是 pagehide 事件,该事件会在浏览器卸载页面的时候触发,而且是在unload 事件之前触发
  1. hashchange 事件
  • 用处:在 URL 的参数列表(及 URL 中“#”号后面的所有字符串)发生变化时通知开发人员。之所以新增这个事件,是因为在 Ajax 应用中,开发人员经常要利用 URL 参数列表来保存状态或导航信息
  • 必须要把 hashchange 事件处理程序添加给 window 对象,然后 URL 参数列表只要变化就会调用它。此时的 event 对象应该额外包含两个属性:oldURL 和 newURL。这两个属性分别保存着参数列表变化前后的完整 URL,注意这两个属性只有Firefox 6+、Chrome 和 Opera 支持,所以还是用location对象来确定当前的参数列表
设备事件,移动端的
触摸与手势事件,移动端

内存与性能

  1. 过多的添加事件处理程序会影响到页面的整体性能,首先,每个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定所有事件处理程序而导致的 DOM 访问次数,会延迟整个页面的交互就绪时间
事件委托
  1. 事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。
  • 使用事件委托,只需在DOM 树中尽量最高的层次上添加一个事件处理程序。原理是由于所有列表项都是这个元素的子节点,而且它们的事件会冒泡,所以单击事件最终会被这个函数处理
  1. 适合采用事件委托技术的事件包括 click、mousedown、mouseup、keydown、keyup 和 keypress。虽然 mouseover 和 mouseout 事件也冒泡,但要适当处理它们并不容易,而且经常需要计算元素的位置。
移除事件处理程序
  1. 前因:
  • 每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的 JavaScript 代码之间就会建立一个连接。这种连接越多,页面执行起来就越慢。
  • 内存中留有那些过时不用的“空事件处理程序”占用内存,影响性能
    • 通过纯粹的 DOM 操作,从文档中移除带有事件处理程序的元素时,例如使用 innerHTML 替换页面中某一部分的时候,原来添加到元素中的事件处理程序极有可能无法被当作垃圾回收
    • 卸载页面的时候,。如果在页面被卸载之前没有清理干净事件处理程序,那它们就会滞留在内存中
  1. 处理1:
  • 如果你知道某个元素即将被移除,那么最好手工移除事件处理程序
<div id="myDiv"> 
 <input type="button" value="Click Me" id="myBtn"> 
</div> 
<script type="text/javascript"> 
 var btn = document.getElementById("myBtn");
 btn.onclick = function(){ 
  //先执行某些操作
  btn.onclick = null; //移除事件处理程序
  document.getElementById("myDiv").innerHTML = "Processing..."; 
  }; 
 </script>
  • 在事件处理程序中删除按钮也能阻止事件冒泡。目标元素在文档中是事件冒泡的前提
  1. 处理2:在页面卸载之前,先通过 onunload 事件处理程序移除所有事件处理程序

模拟事件

DOM中的事件模拟
  1. 模拟事件创建流程
  • 在 document 对象上使用 createEvent()方法创建 event 对象。这个方法接收一个参数,即表示要创建的事件类型的字符串,可以为
    • UIEvents:一般化的 UI 事件。鼠标事件和键盘事件都继承自 UI 事件。DOM3 级中是 UIEvent。
    • MouseEvents:一般化的鼠标事件。DOM3 级中是 MouseEvent。
    • MutationEvents:一般化的 DOM 变动事件。DOM3 级中是 MutationEvent。
    • HTMLEvents:一般化的 HTML 事件。没有对应的 DOM3 级事件(HTML 事件被分散到其他类别中)。
  • 创建了 event 对象之后,还需要使用与事件有关的信息对其进行初始化
  • 最后一步就是触发事件。这一步需要使用 dispatchEvent()方法,需要传入一个参数,即表示要触发事件的 event 对象
  1. 模拟鼠标事件
  • 创建鼠标事件对象的方法是为 createEvent()传入字符串"MouseEvents"。返回的对象有一个名为 initMouseEvent()方法,用于指定与该鼠标事件有关的信息。这个方法接收 15 个参数,分别与鼠标事件中每个典型的属性一一对应[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CMi0lLa3-1569921349923)(JavaScript高级程序设计_files/24.jpg)]
var btn = document.getElementById("myBtn"); 
//创建事件对象
var event = document.createEvent("MouseEvents"); 
//初始化事件对象
event.initMouseEvent("click", true, true, document.defaultView, 0, 0, 0, 0, 0, 
 false, false, false, false, 0, null); 
//触发事件
btn.dispatchEvent(event);
  1. 模拟键盘事件
  2. 模拟其他事件
  3. 自定义DOM事件
IE中的事件模拟
  1. 调用 document.createEventObject()方法可以在 IE 中创建 event 对象,返回一个通用的 event 对象
  2. 为这个对象添加所有必要的信息
  3. 在目标上调用 fireEvent()方法,这个方法接受两个参数:事件处理程序的名称和 event 对象
var btn = document.getElementById("myBtn"); 
//创建事件对象
var event = document.createEventObject(); 
//初始化事件对象
event.screenX = 100; 
event.screenY = 0; 
event.clientX = 0; 
event.clientY = 0; 
event.ctrlKey = false; 
event.altKey = false;
event.shiftKey = false; 
event.button = 0; 
//触发事件
btn.fireEvent("onclick", event);

表单脚本

表单的基础知识

  1. 在 JavaScript 中,表单对应的则是 HTMLFormElement 类型(继承HTMLElement),特有属性和方法:
  • acceptCharset:服务器能够处理的字符集;等价于 HTML 中的 accept-charset 特性。
  • action:接受请求的 URL;等价于 HTML 中的 action 特性。
  • elements:表单中所有控件的集合(HTMLCollection)。
  • enctype:请求的编码类型;等价于 HTML 中的 enctype 特性。
  • length:表单中控件的数量。
  • method:要发送的 HTTP 请求类型,通常是"get"或"post";等价于 HTML 的 method 特性。
  • name:表单的名称;等价于 HTML 的 name 特性。
  • reset():将所有表单域重置为默认值。
  • submit():提交表单。
  • target:用于发送请求和接收响应的窗口名称;等价于 HTML 的 target 特性。
  1. 获取form元素
  • 与其他元素一样,为其添加 id 特性,再使用 getElementById()方法。
  • 通过 document.forms 可以取得页面中所有的表单,在使用索引或name值
var firstForm = document.forms[0]; //取得页面中的第一个表单
var myForm = document.forms["form2"]; //取得页面中名称为"form2"的表单
提交表单
  1. 单击提交按钮或图像按钮时,就会提交表单,以这种方式提交表单时,浏览器会在将请求发送给服务器之前触发 submit 事件
  • 用或都可以定义提交按钮,只要将其 type 特性的值设置为"submit"即可
  • 将的 type 特性值设置为"image"来定义的,点击按钮也可以提交
  1. 在js里,调用 submit()方法也可以提交表单,但不会触发submit 事件
var form = document.getElementById("myForm"); 
//提交表单
form.submit();
  1. 取消表单提交,通过阻止submit 事件默认行为
var form = document.getElementById("myForm"); 
//EventUtil是上一章自定义的,可以方便跨浏览器处理
EventUtil.addHandler(form, "submit", function(event){ 
 //取得事件对象
 event = EventUtil.getEvent(event); 
 //阻止默认事件
 EventUtil.preventDefault(event); 
});
  1. 提交表单容易出现的问题:重复提交(用户不耐烦),可能会造成错误,解决方法:在第一次提交表单后就禁用提交按钮,或者利用 onsubmit 事件处理程序取消后续的表单提交操作
重置表单
  1. 使用 type 特性值为"reset"的或都可以创建重置按钮,点击即重置,触发reset事件,利用这个机会,我们可以在必要时取消重置操作
  2. 使用js重置
var form = document.getElementById("myForm"); 
//重置表单
form.reset();
  1. 阻止重置
var form = document.getElementById("myForm"); 
EventUtil.addHandler(form, "reset", function(event){ 
 //取得事件对象
 event = EventUtil.getEvent(event); 
 //阻止表单重置
 EventUtil.preventDefault(event); 
});
表单字段
  1. 表单有elements属性,该属性是表单中所有表单元素(字段)的集合,这个 elements 集合是一个有序列表,其中包含着表单中的所有字段,。每个表单字段在 elements 集合中的顺序,与它们出现在标记中的顺序相同,可以按照位置和 name 特性来访问它们
  2. 如果有多个表单控件都在使用一个 name(如单选按钮),那么就会返回以该 name 命名的一个NodeList,如果需要访问单个元素,则需要使用角标
  3. 共有的表单字段属性
  • disabled:布尔值,表示当前字段是否被禁用。
  • form:指向当前字段所属表单的指针;只读。
  • name:当前字段的名称。
  • readOnly:布尔值,表示当前字段是否只读。
  • tabIndex:表示当前字段的切换(tab)序号。
  • type:当前字段的类型,如"checkbox"、“radio”,等等。
  • value:当前字段将被提交给服务器的值。对文件字段来说,这个属性是只读的,包含着文件在计算机中的路径。
  1. 共有的表单字段方法:focus()和 blur()
  • focus()方法用于将浏览器的焦点设置到表单字段,即激活表单字段,使其可以响应键盘事件。注意如果是type 特性的值为"hidden"元素或者使用 CSS 的 display 和 visibility 属性隐藏了该字段,使用focus方法会报错
  • HTML5 为表单字段新增了一个 autofocus 属性,是一个布尔值属性,所以在支持的浏览器中它的值应该是 true
  • blur()方法,它的作用是从元素中移走焦点
  1. 共有的表单字段事件
  • blur:当前字段失去焦点时触发。
  • change:对于和元素,在它们失去焦点且 value 值改变时触发;对于元素,在其选项改变时触发。
  • focus:当前字段获得焦点时触发。

文本框脚本

  1. HTML中的文本框:
  • 元素的单行文本框,type 特性设置为"text",通过设置 size 特性,可以指定文本框中能够显示的字符数。通过 value 特性,可以设置文本框的初始值,而 maxlength 特性则用于指定文本框可以接受的最大字符数,用户输入的内容保存在 value 属性中
  • 的多行文本框,rows 特性指定的是文本框的字符行数,而 cols 特性指定的是文本框的字符列数,用户输入的内容保存在 value 属性中
选择文本
  1. select()方法,这个方法用于选择文本框中的所有文本,调用 select()方法时,大多数浏览器(Opera 除外)都会将焦点设置到文本框中。这个方法不接受参数,可以在任何时候被调用
  2. 选择事件,在选择了文本框中的文本时,就会触发 select事件
  3. 取得选择的文本:h5新增两个属性:selectionStart 和 selectionEnd,这两个属性中保存的是基于 0 的数值,表示所选择文本的范围,不过IE8以上不支持,兼容的方法为
//取得选择的文本
function getSelectedText(textbox){ 
if (typeof textbox.selectionStart == "number"){ 
 return textbox.value.substring(textbox.selectionStart, 
 textbox.selectionEnd); 
} else if (document.selection){ 
return document.selection.createRange().text; 
} 
}
  1. 选择部分文本:setSelectionRange()方法。这个方法接收两个参数:要选择的第一个字符的索引和要选择的最后一个字符之后的字符的索引,IE8不支持
//兼容方式
function selectText(textbox, startIndex, stopIndex){ 
 if (textbox.setSelectionRange){ 
 textbox.setSelectionRange(startIndex, stopIndex); 
 } else if (textbox.createTextRange){ //IE8支持
 var range = textbox.createTextRange(); 
 range.collapse(true); 
 range.moveStart("character", startIndex); 
 range.moveEnd("character", stopIndex - startIndex); 
 range.select(); 
 }
  textbox.focus(); 
 }
过滤输入
  1. 屏蔽字符,通过阻止keypress 事件的默认行为来屏蔽此类字符
// 只允许用户输入数值
EventUtil.addHandler(textbox, "keypress", function(event){ 
 event = EventUtil.getEvent(event); 
 var target = EventUtil.getTarget(event); 
 var charCode = EventUtil.getCharCode(event); 
 if (!/\d/.test(String.fromCharCode(charCode)) && charCode > 9){ 
	 /*  \d:0-9的数字,test是正则匹配,String.fromCharCode()将字符编码转换成字符串,charCode > 9是为了兼容火狐和Safari */
	 EventUtil.preventDefault(event);
	 }
	 });
  1. 操作剪贴板,
  • 剪贴板事件:
    • beforecopy:在发生复制操作前触发。
    • copy:在发生复制操作时触发。
    • beforecut:在发生剪切操作前触发。
    • cut:在发生剪切操作时触发。
    • beforepaste:在发生粘贴操作前触发。
    • paste:在发生粘贴操作时触发。
  • 访问剪贴板中的数据,可以使用 clipboardData 对象,在 IE 中,这个对象是 window 对象的属性;而在 Firefox 4+、Safari 和 Chrome 中,这个对象是相应 event 对象的属性,只在发生剪贴板事件期间使用这个对象,它有三个方法
    • getData()用于从剪贴板中取得数据,它接受一个参数,即要取得的数据的格式
    • setData()方法的第一个参数也是数据类型,第二个参数是要放在剪贴板中的文本
自动切换焦点
  1. 在用户填写完当前字段时,自动将焦点切换到下一个字段,在自动切换焦点之前,必须知道用户已经输入了既定长度的数据,比如填那种分成几段的电话号码
html代码
<input type="text" name="tel1" id="txtTel1" maxlength="3"> 
<input type="text" name="tel2" id="txtTel2" maxlength="3"> 
<input type="text" name="tel3" id="txtTel3" maxlength="4">
js代码
(function(){ 
 function tabForward(event){ 
 event = EventUtil.getEvent(event); 
 var target = EventUtil.getTarget(event); 
 if (target.value.length == target.maxLength){ 
 var form = target.form; 
 for (var i=0, len=form.elements.length; i < len; i++) { 
 if (form.elements[i] == target) { 
 if (form.elements[i+1]){ 
 form.elements[i+1].focus(); 
 } 
 return; 
 } 
 } 
 } 
 } 
 var textbox1 = document.getElementById("txtTel1"); 
 var textbox2 = document.getElementById("txtTel2"); 
 var textbox3 = document.getElementById("txtTel3"); 
 EventUtil.addHandler(textbox1, "keyup", tabForward); 
 EventUtil.addHandler(textbox2, "keyup", tabForward); 
 EventUtil.addHandler(textbox3, "keyup", tabForward); 
})();
h5约束验证API(自动验证)
  1. 必填字段:在表单字段中指定了 required 属性,适用于、和字段
  2. 其他输入类型:"email"和"url"类型,"email"类型要求输入的文本必须符合电子邮件地址的模式,而"url"类型要求输入的文本必须符合 URL 的模式
  3. 数值范围:“number”、“range”、“datetime”、“datetime-local”、“date”、“month”、“week”,还有"time"。对所有这些数值类型的输入元素,可以指定 min 属性(最小的可能值)、max 属性(最大的可能值)和 step 属性(从 min 到 max 的两个刻度间的差值),注意这些类型不一定能用
  4. 输入模式:pattern 属性。这个属性的值是一个正则表达式,用于匹配文本框中的值,注意,模式的开头和末尾不用加^和$符号
  5. 检测有效性:
  • 使用 checkValidity()方法可以检测表单中的某个字段是否有效
  • validity 属性则会告诉你为什么字段有效或无效[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZTGyom5-1569921349925)(JavaScript高级程序设计_files/25.jpg)]
  1. 禁用验证:通过在HTML里设置 novalidate 属性,可以告诉表单不进行验证。在 JavaScript 中使用 noValidate 属性可以取得或设置这个值,如果这个属性存在,值为 true,如果不存在,值为 false。

选择框脚本

  1. HTMLSelectElement 类型特有方法
  • add(newOption, relOption):向控件中插入新元素,其位置在相关项(relOption)之前。
  • multiple:布尔值,表示是否允许多项选择;等价于 HTML 中的 multiple 特性。
  • options:控件中所有元素的 HTMLCollection。  remove(index):移除给定位置的选项。
  • selectedIndex:基于 0 的选中项的索引,如果没有选中项,则值为-1。对于支持多选的控件,只保存选中项中第一项的索引。
  • size:选择框中可见的行数;等价于 HTML 中的 size 特性。
  1. 选择框的 type 属性不是"select-one",就是"select-multiple",这取决于 HTML 代码中有没有 multiple 特性
  2. 选择框的 value 属性由当前选中项决定,规则如下
  • 如果没有选中的项,则选择框的 value 属性保存空字符串。
  • 如果有一个选中项,而且该项的 value 特性已经在 HTML 中指定,则选择框的 value 属性等于选中项的 value 特性。即使 value 特性的值是空字符串,也同样遵循此条规则。
  • 如果有一个选中项,但该项的 value 特性在 HTML 中未指定,则选择框的 value 属性等于该项的文本。
  • 如果有多个选中项,则选择框的 value 属性将依据前两条规则取得第一个选中项的值。
  1. HTMLOptionElement 对象属性
  • index:当前选项在 options 集合中的索引。
  • label:当前选项的标签;等价于 HTML 中的 label 特性。
  • selected:布尔值,表示当前选项是否被选中。将这个属性设置为 true 可以选中当前选项。
  • text:选项的文本。
  • value:选项的值(等价于 HTML 中的 value 特性)。
var selectbox = document.forms[0]. elements["location"]; 
//推荐
var text = selectbox.options[0].text; //选项的文本
var value = selectbox.options[0].value; //选项的值
选择选项
  1. 对于单选,访问选中项的方式就是使用选择框的 selectedIndex 属性,多选不适用
//取得选中项和选中项的信息
var selectedOption = selectbox.options[selectbox.selectedIndex];
var selectedIndex = selectbox.selectedIndex; 
var selectedOption = selectbox.options[selectedIndex];
  1. selected 属性设置为 true可以选中选项,selected 属性的作用主要是确定用户选择了选择框中的哪一项。要取得所有选中的项,可以循环遍历选项集合,然后测试每个选项的 selected 属性
//返回给定选择框中选中项的一个数组
function getSelectedOptions(selectbox){ 
 var result = new Array(); 
 var option = null; 
 for (var i=0, len=selectbox.options.length; i < len; i++){ 
 option = selectbox.options[i]; 
 if (option.selected){ 
 result.push(option); 
 } 
 } 
 return result; 
}
// 对这个数组进行操作
var selectbox = document.getElementById("selLocation"); 
var selectedOptions = getSelectedOptions(selectbox); 
var message = ""; 
for (var i=0, len=selectedOptions.length; i < len; i++){ 
 message += "Selected index: " + selectedOptions[i].index + 
 "\nSelected text: " + selectedOptions[i].text + 
 "\nSelected value: " + selectedOptions[i].value + "\n\n"; 
} 
alert(message);
添加选项
  1. 使用DOM 方法
var newOption = document.createElement("option"); 
newOption.appendChild(document.createTextNode("Option text")); 
newOption.setAttribute("value", "Option value"); 
selectbox.appendChild(newOption);
  1. 使用 Option 构造函数来创建新选项,接受两个参数:文本(text)和值(value);第二个参数可选
var newOption = new Option("Option text", "Option value"); 
selectbox.appendChild(newOption); //在 IE8 及之前版本中有问题
  1. 使用选择框的 add()方法,接受两个参数:要添加的新选项和将位于新选项之后的选项,第二个参数传入 undefined,就可以在所有浏览器中都将新选项插入到列表最后了
var newOption = new Option("Option text", "Option value"); 
selectbox.add(newOption, undefined); //最佳方案
  1. 想将新选项添加到其他位置(不是最后一个),就应该使用标准的 DOM 技术和 insertBefore()方法。
移除选项
  1. 使用 DOM 的 removeChild()方法,为其传入要移除的选项
selectbox.removeChild(selectbox.options[0]); //移除第一个选项
  1. 使用选择框的 remove()方法。这个方法接受一个参数,即要移除选项的索引
selectbox.remove(0); //移除第一个选项
  1. 将相应选项设置为 null
selectbox.options[0] = null; //移除第一个选项
  1. 清除选择框中所有的项,需要迭代所有选项并逐个移除它们
function clearSelectbox(selectbox){ 
 for(var i=0, len=selectbox.options.length; i < len; i++){ 
 selectbox.remove(i); 
 } 
}
移动和重排选项
  1. 使用 DOM 的 appendChild()方法,就可以将第一个选择框中的选项直接移动到第二个选择框中,原理是如果为 appendChild()方法传入一个文档中已有的元素,那么就会先从该元素的父节点中移除它,再把它添加到指定的位置
  2. 要将选择框中的某一项移动到特定位置,最合适的 DOM 方法就是 insertBefore()
var optionToMove = selectbox.options[1]; 
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index+2]);

表单序列化

  1. 在表单提交期间,浏览器是怎样将数据发送给服务器的。
  • 对表单字段的名称和值进行 URL 编码,使用和号(&)分隔。
  • 不发送禁用的表单字段。
  • 只发送勾选的复选框和单选按钮。
  • 不发送 type 为"reset"和"button"的按钮。
  • 多选选择框中的每个选中的值单独一个条目。
  • 在单击提交按钮提交表单的情况下,也会发送提交按钮;否则,不发送提交按钮。也包括 type为"image"的元素。
  • 元素的值,就是选中的元素的 value 特性的值。如果元素没有value 特性,则是元素的文本值。
  1. 实现表单序列化的代码
function serialize(form){ 
 var parts = [], 
 field = null, 
 i, 
 len, 
 j, 
 optLen, 
 option, 
 optValue; 
 for (i=0, len=form.elements.length; i < len; i++){ 
 field = form.elements[i]; 
 switch(field.type){ 
 case "select-one": 
 case "select-multiple": 
 if (field.name.length){ 
 for (j=0, optLen = field.options.length; j < optLen; j++){
	 option = field.options[j]; 
	  if (option.selected){ 
	  optValue = ""; 
	  if (option.hasAttribute){ 
	  optValue = (option.hasAttribute("value") ? 
	  option.value : option.text); 
	  } else { 
	  optValue = (option.attributes["value"].specified ? 
	  option.value : option.text); 
	  } 
	  parts.push(encodeURIComponent(field.name) + "=" + 
	  encodeURIComponent(optValue)); 
	  } 
	  } 
	  } 
	  break; 
	  case undefined: //字段集
	  case "file": //文件输入
	  case "submit": //提交按钮
	  case "reset": //重置按钮
	  case "button": //自定义按钮
	  break; 
	  case "radio": //单选按钮
	  case "checkbox": //复选框
	  if (!field.checked){ 
	  break; 
	  } 
	  /* 执行默认操作 */ 
	  default: 
	  //不包含没有名字的表单字段
	  if (field.name.length){ 
	  parts.push(encodeURIComponent(field.name) + "=" + 
	  encodeURIComponent(field.value)); 
	  } 
	  } 
	  } 
	  return parts.join("&"); 
	 }

富文本编辑(WYSIWYG(What You See Is What You Get,所见即所得)。了解一下

  1. 这一技术的本质,就是在页面中嵌入一个包含空 HTML 页面的 iframe。
  • 通过设置 designMode 属性,这个空白的 HTML 页面可以被编辑,而编辑对象则是该页面元素的 HTML 代码。
  • designMode 属性有两个可能的值:“off”(默认值)和"on"。在设置为"on"时,整个文档都会变得可以编辑(显示插入符号),然后就可以像使用字处理软件一样,通过键盘将文本内容加粗、变成斜体,等等
使用contenteditable属性
  1. 把 contenteditable 属性应用给页面中的任何元素,然后用户立即就可以编辑该元素
  2. contenteditable 属性有三个可能的值:"true"表示打开、"false"表示关闭,"inherit"表示
    从父元素那里继承
操作富文本
  1. 与富文本编辑器交互的主要方式,就是使用 document.execCommand()。
  2. 可以为 document.execCommand()方法传递 3 个参数:要执行的命令名称、表示浏览器是否应该为当前命令提供用户界面的一个布尔值(建议始终为false)和执行命令必须的一个值(如果不需要值,则传递 null)。
  3. 预定义命令[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QI8To2pt-1569921349928)(JavaScript高级程序设计_files/26.jpg)],[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aH07VESz-1569921349929)(JavaScript高级程序设计_files/27.jpg)]
富文本选区
  1. 在富文本编辑器中,使用框架(iframe)的 getSelection()方法,可以确定实际选择的文本。这个方法是 window 对象和 document 对象的属性,调用它会返回一个表示当前选择文本的 Selection对象
  2. 每个 Selection 对象都有下列属性。
  • anchorNode:选区起点所在的节点。
  • anchorOffset:在到达选区起点位置之前跳过的 anchorNode 中的字符数量。
  • focusNode:选区终点所在的节点。
  • focusOffset:focusNode 中包含在选区之内的字符数量。
  • isCollapsed:布尔值,表示选区的起点和终点是否重合。
  • rangeCount:选区中包含的 DOM 范围的数量。
  1. Selection 对象的方法,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ERWIobND-1569921349930)(JavaScript高级程序设计_files/28.jpg)]
表单与富文本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值