浏览器对象
window
window对象不但充当全局作用域,而且表示浏览器窗口。它有innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度。内部宽高是指除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。对应的还有outerWidth和outerHeight属性,可以获取整个浏览器窗口的宽高。
navigator
navigator对象表示浏览器的信息,最常用的属性包括:
- navigator.appName:浏览器名称;
- navigator.appVersion:浏览器版本;
- navigator.language:浏览器设置的语言;
- navigator.platform:操作系统类型;
- navigator.userAgent:浏览器设定的User-Agent字符串。
请注意,navigator的信息可以被用户修改,所以JS读取的navigator信息不一定正确。例如:
// 错误写法
var width;
if (getIEVersion(navigator.userAgent) < 9) {
width = document.body.clientWidth;
} else {
width = window.innerWidth;
}
// 正确写法
var width = window.innerWidth || document.body.clientWidth;
screen
screen对象表示屏幕的信息,常用的属性有:
- screen.width:屏幕宽度,以像素为单位;
- screen.height:屏幕高度,以像素为单位;
- screen.colorDepth:返回颜色位数,如8、16、24。
location
location对象表示当前页面的URL信息。例如一个完整的URL:
http://www.example.com:8080/path/index.html?a=1&b=2#TOP
可以用location.href获取。要获得URL各个部分的值,可以这么写:
- location.protocol; // 'http'
- location.host; // 'www.example.com'
- location.port; // '8080'
- location.pathname; // '/path/index.html'
- location.search; // '?a=1&b=2'
- location.hash; // 'TOP'
加载一个新页面调用location.assign(),重新加载当前页面调用location.reload()方法。
document
document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点:
- document的title属性从HTML文档中的
xxx 读取,可以动态改变; - 用document对象提供的getElementById()和getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点;
- document的cookie属性可以获取当前页面的Cookie。
因为HTTP协议是无状态的,服务器为了区分用户在成功登录后,会发送一个Cookie给浏览器,例如user=ABC123XYZ(加密的字符串)...,此后浏览器访问该网站时,会在请求头附上这个Cookie,服务器根据Cookie即可区分出用户。Cookie还可以存储网站的一些设置,例如,页面显示的语言等等。JS通过document.cookie读取到当前页面的Cookie:
document.cookie; // 'v=123; remember=true; prefer=zh'
由于JS能读取到页面的Cookie,而用户的登录信息通常也存在Cookie中,这就造成了巨大的安全隐患。为了确保安全,服务器端在设置Cookie时必须使用httpOnly,设定了httpOnly的Cookie将不能被JS读取,目前主流浏览器均支持httpOnly选项。
history
history对象保存了浏览器的历史记录,JS可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单地调用history.back()可能会降低用户体验,不建议使用history这个对象。
DOM操作
JS中根节点Document已经自动绑定为全局变量document,获取DOM节点最常用的方法是document.getElementById()和document.getElementsByTagName(),以及CSS选择器document.getElementsByClassName()。此外,还有一种方法是使用querySelector()和querySelectorAll(),通过selector语法来获取节点,但不支持低版本的IE:
// 通过querySelector获取ID为q1的节点:
var q1 = document.querySelector('#q1');
// 通过querySelectorAll获取q1节点内的符合条件的所有节点:
var ps = q1.querySelectorAll('div.highlighted > p');
严格地讲,我们这里的DOM节点是指Element,但是DOM节点实际上是Node,在HTML中,Node包括Element、Comment、CDATA_SECTION等很多种,以及根节点Document类型,但是,绝大多数时候我们只关心Element,也就是实际控制页面结构的Node,其他类型的Node忽略即可。
增加DOM
JS中插入新的节点。一个是使用appendChild,把一个子节点添加到父节点的最后一个子节点。例如:
JavaScript
Java
Python
Scheme
// 把JavaScript
添加到的最后一项:
var js = document.getElementById('js'),
list = document.getElementById('list');
list.appendChild(js);
Java
Python
Scheme
JavaScript
如果节点已经存在于当前的文档,那么它会先从原先的位置删除,再插入到新的位置,更多的时候我们会重新创建一个新的节点插入到指定位置。如果我们要把子节点插入到指定的位置,可以使用parentElement.insertBefore(newElement, referenceElement):
Java
Python
Scheme
var list = document.getElementById('list'),
ref = document.getElementById('python'),
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);
Java
Haskell
Python
Scheme
可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候需要循环一个父节点的所有子节点,可以通过迭代children属性实现:
var i, c, list = document.getElementById('list');
for (i = 0; i < list.children.length; i++) {
c = list.children[i]; // 拿到第i个子节点
}
删除DOM
JS中删除一个节点,首先要获得该节点本身以及它的父节点,然后调用父节点的removeChild把自己删掉:
// 拿到待删除节点:
var self = document.getElementById('to-be-removed');
// 拿到父节点:
var parent = self.parentElement;
// 删除:
var removed = parent.removeChild(self);
removed === self; // true
这里要注意删掉的节点虽然不在文档中,但其实它还在内存中,可以随时再次被添加到别的位置。当遍历一个节点的子节点并做删除操作时,要知道children属性是一个只读属性,它在子节点变化时会实时更新,例如:
First
Second
var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 浏览器报错
浏览器报错:parent.children[1]不是一个有效的节点。原因就在于,当First
节点被删除后,parent.children的节点数量已经从2变为了1,索引[1]已经不存在了。
修改DOM
JS中修改节点的方法有两种:
一种是修改innerHTML属性,这个方式非常强大,不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树:
// 获取...
var p = document.getElementById('p-id');
// 设置文本为abc:
p.innerHTML = 'ABC'; // ABC
// 设置HTML:
p.innerHTML = 'ABC RED XYZ';
// ...
的内部结构已修改
用innerHTML时要注意是否需要写入HTML,如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。
第二种是修改innerText或textContent属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标签:
// 获取...
var p = document.getElementById('p-id');
// 设置文本:
p.innerText = '';
// HTML被自动编码,无法设置一个
这种方式的缺点是扰乱了浏览器对form的正常提交,浏览器一般默认点击
注意要return true来告诉浏览器继续提交,如果return false,浏览器将不会继续提交form,这种情况通常对应用户输入有误,提示用户错误信息后终止提交form。
在检查和修改时,要充分利用来传递数据。例如,很多登录表单希望用户输入用户名和密码,但是提交表单时不传输明文密码,而是密码的MD5,直接修改密码框会显示无数个*(因为MD5有32个字符)。要想不改变用户的输入,可以利用实现:
注意没有name属性的的数据不会被提交。
操作文件
JS中上传文件使用的唯一控件是,此时表单的enctype必须指定为multipart/form-data,method必须指定为post,浏览器才能正确编码并以multipart/form-data格式发送表单的数据。
出于安全考虑,浏览器只允许用户点击选取本地文件,用JS对value赋值没有任何效果,JS也无法获得上传文件的真实路径。通常上传的文件都由后台服务器处理,JS可以在提交表单时对文件扩展名做检查,以便防止用户上传无效格式的文件:
var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:\fakepath\test.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') || filename.endsWith('.gif'))) {
alert('Can only upload image file.');
return false;
}
由于JS对用户上传的文件操作非常有限,尤其是无法读取文件内容,使得很多需要操作文件的网页不得不用Flash第三方插件来实现,但H5中新增的File API提供了File和FileReader两个主要对象,允许JS读取文件内容获得更多的文件信息。
var fileInput = document.getElementById('test-image-file'),
info = document.getElementById('test-file-info'),
preview = document.getElementById('test-image-preview');
// 监听change事件:
fileInput.addEventListener('change', function () {
// 清除背景图片:
preview.style.backgroundImage = '';
// 检查文件是否选择:
if (!fileInput.value) {
info.innerHTML = '没有选择文件';
return;
}
// 获取File引用:
var file = fileInput.files[0];
// 获取File信息:
info.innerHTML = '文件: ' + file.name + '
' +
'大小: ' + file.size + '
' +
'修改: ' + file.lastModifiedDate;
if (file.type !== 'image/jpeg' && file.type !== 'image/png' && file.type !== 'image/gif') {
alert('不是有效的图片文件!');
return;
}
// 读取文件:
var reader = new FileReader();
reader.onload = function(e) {
var data = e.target.result; // 'data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...'
preview.style.backgroundImage = 'url(' + data + ')';
};
// 以DataURL的形式读取文件:
reader.readAsDataURL(file);
});
上面的代码演示了如何通过H5的File API读取文件内容。以DataURL的形式读取到的文件是一个字符串,类似于data:image/jpeg;base64,/9j/4AAQSk...(base64编码)...常用于设置图像。如果需要服务器端处理,把字符串base64后面的字符发送给服务器并用Base64解码就可以得到原始文件的二进制信息。
在JS中,浏览器的JS执行引擎总是以单线程模式执行,执行多任务都是通过异步调用来实现,比如上面的代码 reader.readAsDataURL(file)
就会发起一个异步操作来读取文件内容。我们不知道异步操作什么时候会结束,所以要先设置一个回调函数来处理文件:
reader.onload = function(e) {
// 当文件读取完成后,自动调用此函数:
};