《JavaScript高级程序设计》读书笔记 【8章~】【持更】

文章目录

  • 上一篇:《JavaScript高级程序设计》读书笔记 【1~7章】
  • 8. BOM
    • 8.1. window对象
      • 窗口位置
      • 窗口大小
      • 打开窗口
      • 间歇调用与超时调用
      • 系统对话框
    • 8.2. location对象
      • 查询字符串参数
      • 位置操作
    • navigator对象
      • 检测插件
    • 8.3. screen对象
    • 8.4. history对象
  • 9. DOM
    • Node
    • Document
  • 10. DOM扩展
    • 选择符API
    • 元素遍历
    • HTML5
    • 专有扩展
  • 11. DOM2与DOM3
    • DOM2、3的变化
    • 操作样式的DOM API
    • DOM遍历与范围
  • 13. 事件
    • 理解事件流
      • 事件冒泡
      • 事件捕获
      • DOM事件流
    • 使用事件处理程序
    • 事件类型
      • UI事件
        • load、unload事件
        • resize事件
        • 焦点事件
        • 鼠标与滚轮事件
        • 键盘与文本事件

上一篇:《JavaScript高级程序设计》读书笔记 【1~7章】

上一篇:《JavaScript高级程序设计》读书笔记 【1~7章】

8. BOM

BOM提供了很多对象用于访问浏览器的功能。

8.1. window对象

在浏览器中,window是JavaScript访问浏览器窗口的接口,同时也是ECMAScript规定的Global对象。

作为全局对象,全局作用域中声明的变量与方法都是window的属性(var声明)。通过定义全局变量与在window对象上定义属性的区别:前者无法通过delete操作符删除,而后者可以。

尝试访问未声明的变量会报错,但是如果变成查询window对象的属性,就可以通过是否返回undefined来判断变量是否存在。

若页面中包含框架,则每个框架都拥有自己的window对象,且保存在frames集合中;可以通过window.frames[0]window.frames["frameName"]来访问(< frame src="" name=“frameName”>),也可以使用top对象,top对象始终指向最外层的框架,即浏览器窗口(因为window对象往往指向当前框架的实例)。

与top相对地,parent对象指向当前框架的直接上层框架。在没有框架的情况下,top=parent=window。对应地有self对象,始终指向window(可以互换使用,仅作对应意义)

窗口位置

IE、Safari、Opera、Chrome提供screenLeftscreenTop,分别表示相当于屏幕左边和上边的位置,Firefox对应提供screenXscreenY


var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;


var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;

窗口大小

outerWidthouterHeight返回浏览器窗口尺寸,innerWidthinnerHeight返回页面视图大小。document.documentElement/body.clientWidth/Height保存页面视口信息。

取得页面视口大小:

var pageWidth = window.innerWidth,
	pageHeight = window.innerHeight ;
if (typeof pageWidth != "number") {
     
	//是否处于标准模式
	if (document.compatMode == "CSS1Compat") {
     
		pageWidth = document.documentElement.clientWidth;
		pageHeight = document.docunentE1ement.clientHeight;
	} else {
     
		pagewidth = document.body.clientWidth;
		pageHeight = document.body.clientHeight ; 
	}
}

打开窗口

window.open()方法接收四个参数:加载的url、窗口目标、特性字符串与一个是否取代当前页面的布尔值。

一般只传递第一个参数。第二个参数可以是已有窗口或框架的名称,则在该窗口中加载url;否则会新建窗口并命名。第二个参数也可以是特殊的_self(当前框架)、_parent(父级框架)、_top(清除所有被包含的框架并在浏览器窗口加载)、_blank(新窗口)。

可以使用其返回值(对新窗口的引用)是否为null来检测窗口是否被浏览器屏蔽。

间歇调用与超时调用

超时调用使用setTimeOut()方法,接收两个参数:要执行的代码与等待毫秒时间,前者可以传递JS代码字符串但是不建议(导致性能损失)。

setTimeout(function(){
     
	alert("hi");
}, 1000);

等待时间后将任务添加至队列中,若队列非空则需要等待前面的任务执行完再执行。

使用clearTimeOut()方法取消超时调用:需要传入超时调用ID(计划执行代码的唯一标识符,由setTimeOut()方法返回)。

超时调用在全局作用域中执行,函数中的this在非严格模式下是window,严格模式下是undefined

间歇调用类似,会按照指定时间间隔重复执行代码,直至间隔调用取消或页面卸载。设置方法为setInterval(),取消方法clearInterval(),方法与上相同。

例:到达指定值后取消:

var num = 0;
var max = 10;
var intervalId = null;

function increNum(){
     
	num++;
	
	if(num == max){
     
		clearInterval(intervalId);
	}
}

intervalId = setInterval(increNum(),500);

使用超时调用来实现:

var num = 0;
var max = 10;
var intervalId = null;

function increNum(){
     
	num++;
	
	if(num < max){
     
		setTimeout(increNum(),500);
	}
}

setTimeout(increNum(),500);

一般使用超时调用来模拟间歇调用,因为真正的间歇调用有可能在上一次间歇调用结束之前启动,而使用超时调用可以避免。因此不建议使用setInterval。

系统对话框

浏览器通过alert()confirm()prompt()调用系统对话框显示信息,其外观由操作系统或浏览器设置决定,而非css决定。这些方法打开的对话框是同步与模态的:显示对话框时代码会停止执行,关掉后恢复执行。

alert包含文本与OK按钮,confirm包含文本与OK、cancel按钮,通过点击这两个按钮返回true或false。prompt提示用户输入文本,接收两个参数:显示给用户的文本提示与文本输入域的默认值;单击OK返回输入值,否则返回null(其他方式关闭或单击cancel)

var res = prompt("input","sth");
if(res != null){
     
	alert("input: "+res);
}

《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第1张图片

8.2. location对象

location提供与当前窗口中加载的文档有关的信息与一些导航功能。它是window对象的属性,也是document对象的属性(window.location与document.location引用的同一个对象)。它保存当前文档的信息,且将url解析为独立片段。

location对象的属性:

  • hash:返回url中的hash(#号后跟零或多个字符)
  • host:返回服务器名与端口号“www.xxx.com:80”
  • hostname:不带端口号的服务器名“www.xxx.com”
  • href:完整url(toString方法也返回该值)“http:/wwwxxxcom”
  • pathname:返回url中的目录或文件名“/content”
  • port:返回url中指定的端口号“8080”
  • protocol:返回页面使用的协议“http:”或“https:”
  • search:返回url的查询字符串,以问号开头

查询字符串参数

输入search属性可以返回查询内容,但是没有办法逐一访问每个参数;创建解析查询字符串的方法:

function getQueryArgs(){
     
	
	var qs = location.search.length > 0? location.search.substring(1): "";
	var args = {
     };
	var items = qs.length? qs.split("&"): [];
	var item =null, name = null, value = null, i=0, len = items.length;
	
	for(i = 0; i< len; i++){
     
		item = items[i].split("=");
		name = decodeURIComponent(item[0]);
		value = decodeURIComponent(item[1]);
		
		if(name.length){
     
			args[name]= value;
		}
	}
	return args;
}

位置操作

打开新url并在浏览器浏览历史形成新纪录:

location.assign("http://www.xxx.com");

使用以下属性赋值同样会调用assign:

window.location = "http://www.xxx.com";
location.href = "http://ww.xxx.com";

修改location属性值也可以修改当前url,但是会在浏览器浏览历史生成新纪录,后退时回到之前的页面;使用location.replace()方法会替代当前页面而避免上述现象。

reload()方法重新加载当前页面,有可能从缓存中加载,传入true值则从服务器重新加载。

navigator对象

识别客户端浏览器类型。

检测插件

非IE浏览器,plugins数组包含插件信息,每一项都有以下属性:

  • name:插件名
  • description:插件描述
  • filename:插件文件名
  • length:插件处理的MIME类型数量

检测插件:

function hasPlugin(name){
     
	name = name.toLowerCase();
	for(var i=0; i<navigator.plugins.length; i++){
     
		if(navigator.plugins[i].name.toLowerCase().indexOf(name) > -1){
     
			return true;
		}
	}
	return false;
}
alert(hasPlugin("Flash"));

检测IE的插件只能使用ActiveXObject类型,使用唯一标识符来标识COM对象实现的插件:

function hasIEPlugin(name){
     
	try{
     
	//创建未知COM对象会报错
		new ActiveXObject(name);
		return true;
	}catch(e){
     
		return false;
	}
}
alert(hasIEPlugin("ShockwaveFlash.ShockwaveFlash"));

检测插件的典型做法是针对每个插件分别创建函数:

function hasFlash(){
     
	var result = hasPlugins("Flash");
	if(!result){
     
		result = hasIEPlugins("ShockwaveFlash.ShockwaveFlash");
	}
	return result;
}

8.3. screen对象

表明客户端的能力,包括浏览器窗口外部显示器的信息。每个浏览器的screen对象包含不同的属性。

8.4. history对象

保存用户上网的历史记录,从窗口被打开的时刻算起。

使用go()方法在用户的历史记录中任意跳转,传入整数值代表前进/后退几页:

history.go(-2);//后退2页

传入字符串参数会跳转到历史记录中包含该字符串的最近的位置,不包含则什么也不做。

使用history.back()与history.foward()方法模仿浏览器的后退与前进按钮。

这里的浏览历史包含location修改url的操作生成的历史记录。

9. DOM

Node

每个节点有一个childNodes属性,保存NodeList对象(保存一组有序的节点,可以使用位置访问节点且也有length属性但是并不是Array实例)。它是基于DOM结构动态执行查询的结果,DOM结构的变化会直接反映在NodeList对象中。访问其中元素可使用[]item()方法。

将NodeList转换为数组:

function convertArray(nodes){
     
	let array = null;
	try{
     
		array = Array.prototype.slice.call(nodes, 0);
	}catch(e){
     
		//for ~IE8
		array = new Array();
		for(let i=0, len = nodes.length;i<len;i++){
     
			array.push(nodes[i]);
		}
	}
	return array;
}

每个节点都有parentNode属性,指向文档树中的父节点。

previousSiblingnextSibling 属性指向其前一个、后一个兄弟节点。

firstChildlastChild 指向其第一个、最后一个子节点。

appendChild()添加子节点(在尾部追加成为最后一个子节点)。插入特定位置使用insertBefore()方法,接收两个参数:要插入的节点与参照节点,会插入在参照节点之前一个的位置(参照null时为末尾位置)。

replaceChild()方法替换节点,接收两个参数:要插入的节点与被替换的节点;替换后所有的关系指针都被复制过来,旧节点仍在文档中,但已经失去位置。

removeChild()移除节点,接收参数为被移除的节点并作为返回值。被删除的节点在文档中失去位置。

以上需要通过父节点来操作,若在不支持子节点的节点上操作则会报错。还有两个方法是所有类型的节点都的:cloneNode()复制节点,由节点调用,传入一个参数为布尔值来决定是否是深拷贝(true代表深拷贝下复制节点与其子节点树,false代表浅拷贝仅复制节点),返回的节点副本为文档所有,没有指定父节点。normalize()处理文档树中的文本节点(相邻合并、空文本节点删除)。

Document

在浏览器中document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。document对象是window对象的一个属性,可作为全局对象访问。

document对象的documentElement属性指向HTML页面中的< html >元素,也可以通过子节点列表的第一位来获取文档唯一的子节点:

let html = document.documentElement;//获取引用
alert(html === document.childNodes[0]);//true
alert(html === document.firstChild);//true

document对象的body属性指向< body >元素:

var body = document.body; 

document对象的title属性指向< title >元素中的文本,也可以设置该值来更改 title。

document有3个属性与网页请求有关:URL(页面完整的url)、domain(页面域名)、referrrer(来源页面的URL);其中只有domain可以设置,且只能设为url中的父域。不同子域的页面通过设置domain值为相同的值来进行跨域。注意设置为父域后无法再设为子域。

查找元素getElementById()方法接收ID参数区分大小写,需要注意避免表单name属性与其他元素的ID相同。根据标签名的方法getElementByTagName()返回NodeList(HTMLCollection对象,具有namedItem()方法可以通过元素的name获取集合中的项,也可以直接使用方括号按名称访问)。getElementByName()方法通过name属性获取元素。

let myImage = document.getElementsByTagName("img").namedItem("myImage");
let myImage = document.getElementsByTagName("img")["myImage"];

其他的特殊集合

document.write()/writeln()方法写入内容,在页面加载完成后使用会替换页面内容。

操作特性的方法getAttribute()setAttribute()removeAttribute(),获取、设置、移除特性。一般使用自定义属性时使用。attributes属性保存特性列表。

创建元素document.createElement()方法接收一个参数为标签名,创建后为元素添加特性、将元素置入文档中。

向文档中逐个添加项会造成浏览器反复渲染,可以使用文档片段DocumentFragment来保存创建的项,然后一次性添加到文档中。

let fragment = document.createDocumentFragment();

let ul = document.getElementById("myList");
let li = null;

for(let i=0;i<3;i++){
     
	li = document.createElement("li");
	li.appendChild(document.createTextNode("Item "+(i+1)));
	fragment.appendChild(li);
}

ul.appendChild(fragment);

动态脚本

function loadScript(url){
     
	let script = document.createElement("script");
	script.type = "text/javascript";
	script.src = url;
	document.body.appendChild(script);
}

loadScript("client.js");

10. DOM扩展

对DOM的扩展主要是Selectors选择符API和HTML5。

选择符API

querySelector()方法接收CSS选择符,返回与模式匹配的第一个元素,相对地querySelectorAll()方法返回所有匹配的元素(固定的运行时快照)。matchesSelector()方法接收CSS选择符,若调用元素与选择匹配返回true,判断该方法的支持:

《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第2张图片

元素遍历

为DOM元素添加属性:

  • childElementCount:子元素的个数(不包括文本与注释)
  • firstElementChild:第一个子元素,与firstChild比忽略文本注释空格
  • lastElementChild:最后一个元素,对应lastChild
  • previousElementSibling:前一个同辈元素,对应previousSibling
  • nextElementSibling:前一个同辈元素,对应nextSibling

HTML5

getElementByClassName()方法,传入一个字符串作为参数,包含一个或多个类名,使用空格间隔:

let all = document.getElementByClassName("user cur");

classList属性是保存类的列表(包含length、item()方法、方括号语法),此外还包括:

  • add():向列表添加字符串值作为类名,已添加的忽略;
  • contains():列表中存在给定值返回true;
  • remove():在列表中删除指定字符串;
  • toggle():存在则删除,不存在则添加;

焦点管理document.activeElement属性引用DOM中当前获得焦点的元素(页面加载、用户输入、focus()方法),一般页面刚加载完时保存document.body的引用(加载期间为null)。document.hasFocus()方法判断文档是否获得焦点。

HTMLDocument的变化

  1. readyState属性:有两个可能的值:loading(正在加载文档)与complete(加载完毕);
  2. 兼容模式:document.compatMode属性值为“CSS1Compat”(标准模式)与“BackCompat”(混杂模式);
  3. document.head引用元素;

字符集属性document.charset表示文档使用的字符集,defaultCharset表示根据默认浏览器及操作系统设置。

自定义属性:HTML5可以为元素添加非标准的属性,需添加前缀data-

<div id="myDiv" data-appId="123" data-name="nico"></div>

添加自定义属性后可通过元素的dataset属性来访问自定义属性的值,dataset属性值是键值对映射,键名为自定义名除去前缀:

let div = document.getElementById("myDiv");
let appId = div.dataset.appId, name = div.dataset.name;

插入标记
innerHTML属性返回调用元素的所有子节点(包括元素注释与文本),使用字符串作为参数(包含HTML字符串时会经过解析),使用innerHTML插入script元素效果不佳(需要在前面添加有作用域的元素比如文本),一些元素不支持该属性(col/colgroup/framset/head/style/table/tbody/thead/tfoot/tr)。
outerHTML属性返回该节点与其子节点。
insertAdjacentHTML()方法接收两个参数:第一个代表插入位置,为指定值之一:在这里插入图片描述第二个参数是HTML字符串。

内存与性能问题

删除带有事件或引用了其他JS对象的元素时,其绑定关系仍在内存中,需要手动删除。inner/outerHTML属性的一次设置较于多次DOM操作会提高效率,由于会创建HTML解析器,因此避免多次设置该值。

滚动页面scrollIntoView()可在所有HTML元素上调用,滚动页面使元素出现在视口中(比如点击目录与跳到文章段落情景的应用,避免a标签操作url)。传入true或者不传参则元素顶部与视口顶部对齐,传入false则可能底部与视口顶部对齐。(另:传入对象:{
behavior: “auto” | “instant” | “smooth”, // 默认 auto
block: “start” | “center” | “end” | “nearest”, // 默认 center
inline: “start” | “center” | “end” | “nearest”, // 默认 nearest
})

element.scrollIntoView({
     behavior: "smooth", block: "end", inline: "nearest"});

专有扩展

children属性:与childNodes对标但只包含其中的元素子节点。

contains()方法:判断参数节点是否为调用节点的后代。

11. DOM2与DOM3

DOM2、3的变化

扩展DOM API以满足XML要求,提供错误处理与特性检测能力。2级增强现有类型,3级增强并添加了新类型。

命名空间:xmlns="http://www.w3.org/1999/xhtml" 应当被包含在html标签中。

通过命名空间将svg独立并有效:
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第3张图片DOM2中命名空间相关属性:localName(不带去命名空间前缀的节点名)、namespaceURI(命名空间URI)、prefix(命名空间前缀)

Document类型新增命名空间相关方法:
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第4张图片相对应的Element对象也有命名空间相关改动。

DOM3中命名空间相关方法:
在这里插入图片描述

操作样式的DOM API

css属性转换为JS属性时格式转为驼峰式命名,float为JS保留字因此无法转换,其对应值为cssFloat(IE为styleFloat)。

混杂模式下“20px”可以写为“20”,标准模式不可以(因为缺少度量单位)

CSSText属性会返回/覆写元素的整个css样式。

与css属性相关的方法:

  • removeProperty (propertyName):从样式中删除给定属性。
  • setProperty (propertyName, value, priority) :将给定属性设置为相应的值,并加上优先权标志(" important "或者一个空串)。

document.styleSheets保存样式表列表。insertRule(规则文本,插入位置索引)方法向其中添加新规则,removeRule()方法删除规则。

偏移量

  • offsetHeight:元素在垂直方向上占用的空间大小,以像素计。
  • offsetwidth:元素在水平方向上占用的空间大小,以像素计。
  • offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
  • offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。

《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第5张图片
偏移量只读,且每次访问重新计算,避免多次取用。

客户区大小:(client)内容+内边距:
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第6张图片
滚动大小

  • scrollHeight:在没有滚动条的情况下,元素内容的总高度。
  • scrollwidth:在没有滚动条的情况下,元素内容的总宽度。
  • scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
  • scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。

《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第7张图片

确定文档高度时,应取scrollHeight与clientHeight的最大值。

DOM遍历与范围

2级定义了NodeIteratorTreeWalker两个基于给定起点对DOM结构深搜的遍历操作。IE不支持。
【待扩展的部分】347页起。

13. 事件

理解事件流

事件流描述从页面中接收事件的顺序。

事件冒泡

IE的事件流为事件冒泡,事件开始时由具体的元素接收,逐级向上传播至文档:
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第8张图片~IE5.5会跳过< html>元素,由body直接跳到document;IE9、Firefox、Chrome、Safari会将事件一直冒泡到window对象。

事件捕获

由document捕获事件,沿DOM树依次向下传递至实际目标,与冒泡顺序相反。一般使用冒泡,特殊使用捕获。

DOM事件流

DOM2级规定事件流有三个阶段:事件捕获阶段、处于目标阶段与事件冒泡阶段
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第9张图片

使用事件处理程序

click、load、mouseover等由用户和浏览器自身执行的动作是事件,响应事件的函数叫做事件处理函数事件侦听器。以“on”开头。

事件对象event:

<input type="button" onclick="alert(event.type)">
//click

其有currentTarget属性指向当前正在处理事件的元素。

阻止事件的默认行为(比如链接点击跳转等)可以使用preventDefault()方法(当其cancelable属性为true时才可取消):

link.onclick = function(event){
     
	event.preventDefault();
};

立即停止事件在DOM层级中的传播、取消进一步的捕获或冒泡使用event.stopPropagation()方法。

IE中的事件对象event是window的一个属性:使用window.event取得

this等同于事件的目标元素:

var btn = document.getElementById("button");
btn.onclick = function(){
     
	alert(this.id);//button
}

DOM2级定义两个方法处理/删除事件处理程序:addEventListener()removeEventListener()

var btn = document.getElementById("button");
btn.addEventListener("click", function(){
     
	alert(this.id);
}, false);

所有的DOM节点都包含这两个方法,第三个参数传入布尔值,true代表 捕获阶段 调用事件处理程序,false冒泡阶段 处理。若添加多个同类型事件的处理程序会依次触发。经过addEventListener()添加的处理程序只能使用removeEventListener()来移除,且参数要相同。

IE提供类似的attachEvent()detachEvent()方法,只传前两个参数,都在冒泡阶段处理。其事件名带on前缀。DOM0级方法的this指向调用元素(所属元素),而这两个方法的事件处理程序会在全局作用域中运行,因此this等于window。多个同类型事件的程序会反向依次触发。支持的有IE和Opera。

由于this的不确定性,建议使用event.srcElement属性替代。

跨浏览器的场合
《JavaScript高级程序设计》读书笔记 【8章~】【持更】_第10张图片

事件类型

DOM3级规定的事件类型:

  • UI:用户与页面元素交互触发
  • 焦点:获得/失去焦点
  • 鼠标:通过鼠标执行操作触发
  • 滚轮:滚轮或类似设备触发
  • 文本:输入文本
  • 键盘:通过键盘执行操作
  • 合成:输入法编辑器输入字符
  • 变动:底层DOM结构变化

UI事件

不一定是与用户操作有关,包括:

  • load/unload:完全加载/卸载页面在window上触发(图像、框架、嵌入内容加载完毕在该元素上触发)
  • abort:用户停止下载时若嵌入内容未加载完则在对应object元素触发
  • error:JS发送错误在window触发,无法加载的内容对应元素触发
  • select:用户选择文本框(input/textarea)中的字符触发
  • resize:窗口/框架大小变化在window/框架触发
  • scroll:用户滚动带滚动条内容在元素上触发

load、unload事件

图片触发load事件:

<img src="s.png" onload="alert('load')">

或者:

let img = document.getElementById("img");
img.onload = function(){
     };
img.addEventListener("load",function(){
     },false);

unload对应onunload;一般用于清除引用避免内存泄漏。

resize事件

Firefox在用户停止调整窗口时触发resize,而其他浏览器在任何变化时触发。窗口最大/小化也会触发resize事件。

焦点事件

以下焦点事件:

  • blur:失去焦点触发,不会冒泡
  • focus:获得焦点,不会冒泡
  • focusin:与focus等价,会冒泡
  • focusout:与blur通用

鼠标与滚轮事件

鼠标事件:

  • click:单击或回车
  • dbclick:双击
  • mousedown:按下任意鼠标按钮
  • mouseup:释放鼠标按钮触发
  • mouseenter:光标首次移入元素范围(不包含后代、不冒泡)
  • mouseleave:光标由元素上移出范围(不包含后代、不冒泡)
  • mousemove:光标在元素内移动反复触发
  • mouseout:由一个元素移入另一个元素
  • mouseover:光标首次移入边界

mousedown+up触发over,两次click触发dbclick,依赖关系中任一事件被取消则不会触发。

按下鼠标时键盘上的某些状态可以影响操作:shiftKey、ctrlKey、altKey、metaKey,代表相应的键被按下(true值)。

键盘与文本事件

keydown/keyup:任意键按下/释放
keypress:任意字符键按下

按住不放会重复触发按下事件

你可能感兴趣的:(前端,javascript,js,web,前端)