上一篇:《JavaScript高级程序设计》读书笔记 【1~7章】
BOM提供了很多对象用于访问浏览器的功能。
在浏览器中,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提供screenLeft
与screenTop
,分别表示相当于屏幕左边和上边的位置,Firefox对应提供screenX
与screenY
:
var leftPos = (typeof window.screenLeft == "number") ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ? window.screenTop : window.screenY;
outerWidth
、outerHeight
返回浏览器窗口尺寸,innerWidth
、innerHeight
返回页面视图大小。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);
}
location提供与当前窗口中加载的文档有关的信息与一些导航功能。它是window对象的属性,也是document对象的属性(window.location与document.location引用的同一个对象)。它保存当前文档的信息,且将url解析为独立片段。
location对象的属性:
输入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值则从服务器重新加载。
识别客户端浏览器类型。
非IE浏览器,plugins数组包含插件信息,每一项都有以下属性:
检测插件:
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;
}
表明客户端的能力,包括浏览器窗口外部显示器的信息。每个浏览器的screen对象包含不同的属性。
保存用户上网的历史记录,从窗口被打开的时刻算起。
使用go()方法在用户的历史记录中任意跳转,传入整数值代表前进/后退几页:
history.go(-2);//后退2页
传入字符串参数会跳转到历史记录中包含该字符串的最近的位置,不包含则什么也不做。
使用history.back()与history.foward()方法模仿浏览器的后退与前进按钮。
这里的浏览历史包含location修改url的操作生成的历史记录。
每个节点有一个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
属性,指向文档树中的父节点。
previousSibling
与 nextSibling
属性指向其前一个、后一个兄弟节点。
firstChild
与 lastChild
指向其第一个、最后一个子节点。
appendChild()
添加子节点(在尾部追加成为最后一个子节点)。插入特定位置使用insertBefore()
方法,接收两个参数:要插入的节点与参照节点,会插入在参照节点之前一个的位置(参照null时为末尾位置)。
replaceChild()
方法替换节点,接收两个参数:要插入的节点与被替换的节点;替换后所有的关系指针都被复制过来,旧节点仍在文档中,但已经失去位置。
removeChild()
移除节点,接收参数为被移除的节点并作为返回值。被删除的节点在文档中失去位置。
以上需要通过父节点来操作,若在不支持子节点的节点上操作则会报错。还有两个方法是所有类型的节点都的:cloneNode()
复制节点,由节点调用,传入一个参数为布尔值来决定是否是深拷贝(true代表深拷贝下复制节点与其子节点树,false代表浅拷贝仅复制节点),返回的节点副本为文档所有,没有指定父节点。normalize()
处理文档树中的文本节点(相邻合并、空文本节点删除)。
在浏览器中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.anchors
:所有带name
特性的
元素;document.forms
:所有< form >
元素;document.images
:所有< image >
元素;document.links
:所有带href
特性的< a >
元素。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");
对DOM的扩展主要是Selectors选择符API和HTML5。
querySelector()
方法接收CSS选择符,返回与模式匹配的第一个元素,相对地querySelectorAll()
方法返回所有匹配的元素(固定的运行时快照)。matchesSelector()
方法接收CSS选择符,若调用元素与选择匹配返回true,判断该方法的支持:
为DOM元素添加属性:
childElementCount
:子元素的个数(不包括文本与注释)firstElementChild
:第一个子元素,与firstChild
比忽略文本注释空格lastElementChild
:最后一个元素,对应lastChild
previousElementSibling
:前一个同辈元素,对应previousSibling
nextElementSibling
:前一个同辈元素,对应nextSibling
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的变化:
readyState
属性:有两个可能的值:loading
(正在加载文档)与complete
(加载完毕);document.compatMode
属性值为“CSS1Compat
”(标准模式)与“BackCompat
”(混杂模式);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()
方法:判断参数节点是否为调用节点的后代。
扩展DOM API以满足XML要求,提供错误处理与特性检测能力。2级增强现有类型,3级增强并添加了新类型。
命名空间:xmlns="http://www.w3.org/1999/xhtml"
应当被包含在html标签中。
通过命名空间将svg独立并有效:
DOM2中命名空间相关属性:localName
(不带去命名空间前缀的节点名)、namespaceURI
(命名空间URI)、prefix
(命名空间前缀)
Document类型新增命名空间相关方法:
相对应的Element对象也有命名空间相关改动。
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
:元素的上外边框至包含元素的上内边框之间的像素距离。scrollHeight
:在没有滚动条的情况下,元素内容的总高度。scrollwidth
:在没有滚动条的情况下,元素内容的总宽度。scrollLeft
:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。scrollTop
:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。确定文档高度时,应取scrollHeight与clientHeight的最大值。
2级定义了NodeIterator
与TreeWalker
两个基于给定起点对DOM结构深搜的遍历操作。IE不支持。
【待扩展的部分】347页起。
事件流描述从页面中接收事件的顺序。
IE的事件流为事件冒泡,事件开始时由具体的元素接收,逐级向上传播至文档:
~IE5.5会跳过< html>元素,由body直接跳到document;IE9、Firefox、Chrome、Safari会将事件一直冒泡到window对象。
由document捕获事件,沿DOM树依次向下传递至实际目标,与冒泡顺序相反。一般使用冒泡,特殊使用捕获。
DOM2级规定事件流有三个阶段:事件捕获阶段、处于目标阶段与事件冒泡阶段。
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
属性替代。
DOM3级规定的事件类型:
不一定是与用户操作有关,包括:
图片触发load事件:
<img src="s.png" onload="alert('load')">
或者:
let img = document.getElementById("img");
img.onload = function(){
};
img.addEventListener("load",function(){
},false);
unload对应onunload;一般用于清除引用避免内存泄漏。
Firefox在用户停止调整窗口时触发resize,而其他浏览器在任何变化时触发。窗口最大/小化也会触发resize事件。
以下焦点事件:
鼠标事件:
mousedown+up触发over,两次click触发dbclick,依赖关系中任一事件被取消则不会触发。
按下鼠标时键盘上的某些状态可以影响操作:shiftKey、ctrlKey、altKey、metaKey,代表相应的键被按下(true值)。
keydown/keyup:任意键按下/释放
keypress:任意字符键按下
按住不放会重复触发按下事件