目前的浏览器有以下几种
IE 6~11: 对W3C支持差, 从IE10开始支持ES6
Chrome: 谷歌的浏览器, Webkit内核, JS引擎是十分强悍的V8, 保持自动升级的模式
Safari: 苹果的浏览器, 同样是Webkit内核
Firefox: Mozilla自己研发的Gecko内核, JS引擎也是自己的OdinMonkey
1 浏览器对象
(1) window
表示全局作用域, 也表示浏览器窗口
window.innerWidth 显示网页的净宽度
window.innerHeight 显示网页的净高度
window.outerWidth 浏览器整个宽度
window.outerHeight 浏览器整个高度
(2) navigator
表示浏览器信息
navigator.appName:浏览器名称;
navigator.appVersion:浏览器版本;
navigator.language:浏览器设置的语言;
navigator.platform:操作系统类型;
navigator.userAgent:浏览器设定的User-Agent字符串。
然而navigator的信息很容易就被用户修改, 所以一般不同navigator判断浏览器类型等信息
一般判断不同的属性要抓住JavaScript对不存在的内容不是报错而是返回undefined
var width = window.innerWidth || document.body.clientWidth;
(3) screen
表示屏幕信息
screen.width:屏幕宽度,以像素为单位;
screen.height:屏幕高度,以像素为单位;
screen.colorDepth:返回颜色位数,如8、16、24。
(4) location
表示当前页面的URL信息
(5) document
表示当前页面, 是整个DOM树的根节点
可以根据document来获取页面的所有标签
可以通过title来修改页面的title
可以通过getElementById()和getElementsByTagName()等来获取标签对象
document对象还有一个十分关键的cookie
Cookie是服务器发送的键值对标识符, 浏览器通过将Cookie发送给服务器, 让服务器认识当前的浏览器, 改善HTTP无状态的情况
由于一般JavaScript也可以读取Cookie, 这样就十分不安全, 为此可以在服务器中设置httpOnly, 这样cookie就不能被JavaScript读取了
(6) history
保存了浏览器的历史记录
可以使用back()和forward()前进和后退
但是这种简单粗暴的方式应该杜绝使用了
2 操作DOM
HTML文档被浏览器解析之后就是一棵DOM树
对DOM节点有基本的四个操作: 增删改查
(1) 常用方法
document.getElementById() 根据Id, 获得唯一元素
document.getElementsByTagName() 返回一组元素
document.getElementsByClassName() CSS选择器, 返回一组元素
(2) 通过selector选择
querySelector() 可以是document调用, 传入选择器参数
querySelectorAll() 可以是获得的元素对象, 传入的同样是选择器参数
2.1 更新DOM
(1) 更改标签内容
更改标签内容有三个值: innerHTML, innerText, textContent
innerHTML是获取标签所有内容, 并且可以给其重新赋值, 赋值的内容可以包含有效标签
innerText获取的是元素对象里面的文本信息, 如果给其重新赋值, 会覆盖原有内容, 不会生成有效的标签
textContent与innerText差不多, 只是返回内容的值全部拼接到了一起
(2) 更新标签的属性
可以直接使用
标签对象.属性名 = "修改后的值"
来直接修改内容, 如果原有内容不符合命名规则, 需要将其转化为驼峰写法
PS: 添加属性使用setAttribute(属性名, 属性值)
2.2 插入DOM
使用innerHTML尽管可以添加一些新的标签进去, 但是会替换原有的内容
插入DOM主要是有两个方法appendChild()和insertBefore(新加入节点, 标志节点)
可以使用document.createElement(标签名字)来创建一个标签
通过setAttribute()和直接操作属性, innerHTML等来修改得到标签
给head添加style标签如下
var d = document.createElement('style');
d.setAttribute('type', 'text/css');
d.innerHTML = 'p { color: red }';
document.getElementsByTagName('head')[0].appendChild(d);
新添加一个元素到python元素之前
//原来的HTML
//<div id="list">
// <p id="java">Java</p>
// <p id="python">Python</p>
// <p id="scheme">Scheme</p>
//</div>
var
list = document.getElementById('list'),
ref = document.getElementById('python'),
haskell = document.createElement('p');
haskell.id = 'haskell';
haskell.innerText = 'Haskell';
list.insertBefore(haskell, ref);
//新增后的HTML
//<div id="list">
// <p id="java">Java</p>
// <p id="haskell">Haskell</p>
// <p id="python">Python</p>
// <p id="scheme">Scheme</p>
//</div>
2.3 删除DOM
删除DOM的办法是, 先获得要删除的节点, 然后获取该节点的父节点, 然后调用父节点的removeChild()删除自身
删除后会有一个返回值, 这个返回值就是被删除的节点, 这个节点会继续保存在内存当中, 因此可以通过这个返回值可以将它放到另一个地方
但是这又有一个十分诡异的情况:
children属性是一个只读属性, 当子节点发生变化的时候会实时更新
也就是说:
var parent = document.getElementById('parent');
parent.removeChild(parent.children[0]);
parent.removeChild(parent.children[1]); // <-- 浏览器报错
因此在删除多个节点的时候, 要注意children时刻都在变化
3 操作表单
表单本身也是一个DOM树, 因此对表单的操作与DOM类似
HTML的输入控件一般有
文本框,对应的
<input type="text">
,用于输入文本;口令框,对应的
<input type="password">
,用于输入口令;单选框,对应的
<input type="radio">
,用于选择一项;复选框,对应的
<input type="checkbox">
,用于选择多项;下拉框,对应的
<select>
,用于选择一项;隐藏文本,对应的
<input type="hidden">
,用户不可见,但表单提交时会把隐藏文本发送到服务器。
(1) 一般对表单的操作是设置和获取标签的值, 一般是获得标签之后使用
标签对象.value;
标签对象.value = 新设置的值;
来获取或设置
但是例如radio这样的, 应该获取checked的值来判断状态, 一般来说勾选为true不勾选为false
(2) 提交表单
方法1: 设置一个提交按钮, 写触发的点击事件, 获取表单, 用 表单对象.submit() 来提交表单
//<form id="test-form">
// <input type="text" name="test">
// <button type="button" onclick="doSubmitForm()">Submit</button>
//</form>
function doSubmitForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 提交form:
form.submit();
}
但是正常情况是使用<button type="submit">或者在某个输入框回车的时候提交表单
方法2: 在表单中添加onsubmit属性, 里面使用"return 检验函数()", 这样在提交函数内部会有一个返回值, 当返回为true时表单提交, 返回为假时表单不提交
//<form id="test-form" onsubmit="return checkForm()">
// <input type="text" name="test">
// <button type="submit">Submit</button>
//</form>
function checkForm() {
var form = document.getElementById('test-form');
// 可以在此修改form的input...
// 继续下一步:
return true;
}
表单注意事项:
要提交的表单要写好name属性, 没有name属性是不会提交的, 这可以用于不传输明文密码而把原密码加密后给一个hidden的input
4 操作文件
唯一可以上传文件的控件是<input type="file">
当上传文件的时候, 需要将表单的enctype设置为multipart/form-data, methodno必须设置为post
出于安全性设计, JavaScript是无法获取文件的真实路径的, 也无法赋值, 但是可以使用value来获得假的路径, 文件名是正常的
一般来说都是对上传的文件作一个类型判断
var f = document.getElementById('test-file-upload');
var filename = f.value; // 'C:fakepath est.png'
if (!filename || !(filename.endsWith('.jpg') || filename.endsWith('.png') || filename.endsWith('.gif'))) {
alert('Can only upload image file.');
return false;
}
由于JavaScript本身对文件的操作有限, 可以使用File API来获取更多文件信息和一些操作
HTML5的File API提供了File和FileReader两个主要对象, 可以获得文件信息并读取文件
以DataURL的形式读取到的文件是一个字符串, 常用于设置图像, 如果服务器处理, 把字符串base64后面的字符发送给服务器并用Base64解码就可以得到原始文件的二进制内容
JavaScript有一个十分十分十分重要的特点, 就是单线程执行模式
也就是说, 浏览器在执行JS代码的时候总是以单线程模式执行, 任何时候JS代码都不可能同时有多余1个线程在执行
JS处理多任务的方法是使用异步调用, 对于FileReader对象, 就需要先设置一个FileReader对象.onload为一个编写好的回调函数
这个回调函数在FileReader对象完成文件读取之后才会执行, 这就是JS中比较神奇的异步调用了
读入一个图片文件并将图片和图片信息显示的实例代码如下:
var
fileInput = document.getElementById('test-image-file'),//文件获取input
info = document.getElementById('test-file-info'),//文件信息展示div
preview = document.getElementById('test-image-preview');//图片展示div
// 监听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 + '<br>' +
'大小: ' + file.size + '<br>' +
'修改: ' + 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);
});
5 AJAX
AJAX(Asynchronous JavaScript and XML) JavaScript执行异步网络请求
web运行的原理: 一次HTTP请求对应一个页面
如果需要停留在当前页面中, 并且还发送HTTP请求, 这就需要使用JavaScript来发送这个请求, 就收到数据后, 在用JavaScript更新页面
现代的AJAX主要是依靠XMLHttpRequest对象, 但是对于低版本的IE, 需要使用ActiveXObjective对象
但是判断IE版本不要使用navigator不要使用navigator不要使用navigator, 而要抓住JavaScript中的属性不存在的时候返回undefined
一般地:
获取到一个请求对象, 然后设置对象.onreadystatechange为一个回调函数, 再使用open()函数设置请求, 使用send()函数发送请求
其中回调函数一般先判断 对象.readyState==4 表示请求是否完成, 再用status==200判断是否是成功响应
对于open(), 有三个参数, 分别是发送请求方式(POST/GET), URL地址, 是否使用异步(默认为true)
特别注意, 如果将是否使用异步设置为false, name浏览器会停止相应等待请求完成, 如果超过10秒, 浏览器就会处于一个假死状态
具体的兼容版本代码如下:
function success(text) {
var textarea = document.getElementById('test-response-text');
textarea.value = text;
}
function fail(code) {
var textarea = document.getElementById('test-response-text');
textarea.value = 'Error code: ' + code;
}
// 新建XMLHttpRequest或者Microsoft.XMLHTTP对象
var request;
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
request = new ActiveXObject('Microsoft.XMLHTTP');
}
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成
// 判断响应结果:
if (request.status === 200) {
// 成功,通过responseText拿到响应的文本:
return success(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(request.status);
}
} else {
// HTTP请求还在继续...
}
}
// 发送请求:
request.open('GET', '/api/categories');
request.send();
alert('请求已发送,请等待响应...');
关于安全限制
对于URL设置, 需要设置为当前页面完全一致的域名的URL, 这是浏览器的同源策略
完全一致包括: 域名相同, 协议相同, 端口相同
解决同源问题有三种办法:
1) 通过Flash插件来发送HTTP请求, 但是与Flash交互不是很方便, 当下用得也少
2) 在同源于域名下, 架设一个代理服务器, 通过该代理服务器来转发请求
'/proxy?url=http://www.sina.com.cn'
3) JSONP, 只能使用GET请求, 并且要求返回JavaScript, 实际上是利用浏览器允许跨域引用JavaScript资源来实现的
具体操作方法为:
用js添加<script>标签, 里面src就是外部的引用地址, 然后根据该js返回的内容, 设置一个回调函数(一般地, 返回值的数据名部分就是回调函数的名字)
CORS(Cross-Origin Resource Sharing) 是HTML5规范定义的如何跨域访问资源
当JavaScript向外域发送请求后, 浏览器会回应请求, 请求中有Access-Control-Allow-Origin字段, 如果该字段中包含了本域(或者为*), 则此次跨域请求成功
由此可见, 跨域请求是否成功还是取决于请求的服务器是否同意此次跨域, 这也就解释了有的使用使用引用外部的字体的时候, 有可能没有引用成功导致字体设置不正常
当然对于POST和DELETE等的POST请求, 在发送AJAX请求之前, 浏览器回先发送一个OPTIONS请求到这个URL上, 询问服务器是否接受, 且服务器相应还会给出允许的Method
6 Promise
JavaScript的单线程的特性也就意味着JS代码只能是异步执行, 异步执行的话就需要使用回调函数来实现具体功能
由于在回调中, 会出现成功失败等各种情况的处理结果, 因此需要写各个情况的处理函数, 这样也不方便重用, 现在有一个类似jq中的链式写法
可以直接在请求之后直接跟上成功和失败的处理函数, 这样就不需要考虑是否成功与失败, 直接自己调用对应的回调函数了
Promise是封装执行代码和处理结果, 将其分离
具体实例如下
// 清除log:
var logging = document.getElementById('test-promise-log');
while (logging.children.length > 1) {
logging.removeChild(logging.children[logging.children.length - 1]);
}
// 输出log到页面:
function log(s) {
var p = document.createElement('p');
p.innerHTML = s;
logging.appendChild(p);
}
new Promise(function (resolve, reject) {
log('start new Promise...');
var timeOut = Math.random() * 2;
log('set timeout to: ' + timeOut + ' seconds.');
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}).then(function (r) {
log('Done: ' + r);
}).catch(function (reason) {
log('Failed: ' + reason);
});
利用Promise可以将多个设置多个异步任务, 当执行任务1之后再执行任务2, 任何一个任务失败都不会继续执行
另外组合使用Promise, 就可以实现异步任务以并行和串行的方式组合起来执行
7 Canvas
Canvas是HTML5新增的组件, 类似于幕布的效果, 可以在Canvas上绘制各种图表, 动画等
可以在Canvas中定义一些HTML标签, 用于在有的浏览器不支持Canvas的时候显示定义好的这些标签
Canvas标签含有属性width和height用于定义尺寸大小
可以通过获得的Canvas对象.getContext()来判断浏览器是不是支持Canvas
获得绘图2d或者3d对象
Canvas对象.getContext("2d")
Canvas对象.getContext("webgl")
Canvas的二维绘图有一个坐标轴, 0点是左上角, 水平向右是x轴, 垂直向下是y轴
具体在Canvas上绘制一个灰色背景的笑脸图像的具体代码如下
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, 200, 200); // 擦除(0,0)位置大小为200x200的矩形,擦除的意思是把该区域变为透明
ctx.fillStyle = '#dddddd'; // 设置颜色
ctx.fillRect(10, 10, 130, 130); // 把(10,10)位置大小为130x130的矩形涂色
// 利用Path绘制复杂路径:
var path=new Path2D();
path.arc(75, 75, 50, 0, Math.PI*2, true);
path.moveTo(110,75);
path.arc(75, 75, 35, 0, Math.PI, false);
path.moveTo(65, 65);
path.arc(60, 65, 5, 0, Math.PI*2, true);
path.moveTo(95, 65);
path.arc(90, 65, 5, 0, Math.PI*2, true);
ctx.strokeStyle = '#0000ff';
ctx.stroke(path);
绘制文本
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = '#666666';
ctx.font = '24px Arial';
ctx.fillStyle = '#333333';
ctx.fillText('带阴影的文字', 20, 40);