第五章-浏览器

目前的浏览器有以下几种

  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\test.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);

 

posted @ 2017-06-01 09:13  weihuchao  阅读(282)  评论(0编辑  收藏  举报