JS-Web-API —— 事件,Ajax,存储

事件


DOM0级和 DOM2级方法指定的事件处理程序都被认为是元素的方法。所以事件处理程序是在元素的作用域中运行,即程序中的 this引用当前元素

// DOM0
var btn1 = document.getElementById('btn1');
btn1.onclick = function () {
	console.log(this.id); // btn1
}

// DOM2
var btn2 = document.getElementById('btn2');
btn2.addEventListener('click', function () {
	console.log(this.id); // btn2
}, false);
// 第三个参数为 true,表示捕获阶段调用事件处理程序
// 如果为 false,表示在冒泡阶段调用事件处理程序

使用 DOM2级方法添加事件处理程序的主要好处是可以添加多个事件处理程序。


通用的事件监听函数
/**
 * 通用的事件监听函数
 *
 * @param {Node} elem 绑定事件的元素
 * @param {String} type 绑定事件的类型
 * @param {String} selector 代理的标签的字符串
 * @param {Function} fn 绑定的回调函数
 */
function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector;
    selector = null;
  }
  elem.addEventListener(type, function (e) {
    var target;
    if (selector) {
      target = e.target;
      if (target.matches(selector)) {
        fn.call(target, e);
      }
    } else {
      fn(e);
    }
  })
}

// 使用代理
var box = document.getElementById('box');
bindEvent(box, 'click', 'a', function (e) {
  alert(this.innerHTML);
})

// 不使用代理
var a1 = document.getElementById('a1');
bindEvent(a1, 'click', function (e) {
  alert(e.target.innerHTML);
  e.stopPropagation();
})

事件流


事件冒泡

IE的事件流叫做事件冒泡。即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。所有现代浏览器都支持事件冒泡,并且会将事件一直冒泡到 window对象。

  • 冒泡的应用:使用代理(例如给包含多个元素的容器绑定事件处理程序)

事件捕获

事件捕获的思想是不太具体的节点应该更早的接收到事件,而在最具体的节点应该最后接收到事件。事件捕获的用以在于事件到达预定目标之前捕获它。


DOM 事件流

“DOM2级事件”规定事件流包括三个阶段,事件捕获阶段、处于目标阶段和事件冒泡阶段。首先发生的事件捕获,为截获事件提供了机会。然后是实际的目标接收了事件。最后一个阶段是冒泡阶段,可以在这个阶段对事件做出响应。

  • 阻止冒泡:stopPropagation()
  • 取消事件的默认行为:preventDefault()
一个有趣的例子

原文链接:你真的理解事件冒泡和事件捕获吗?

<div id="a">
    <div id="b">
        <div id="c"></div>
    </div>
</div>
#a{
    width: 300px;
    height: 300px;
    background: pink;
}
#b{
    width: 200px;
    height: 200px;
    background: blue;
}
#c{
    width: 100px;
    height: 100px;
    background: yellow;
}
var a = document.getElementById("a"),
    b = document.getElementById("b"),
    c = document.getElementById("c");
c.addEventListener("click", function (event) {
    console.log("c1");
    // 注意第三个参数没有传进 false , 因为默认传进来的是 false
    //,代表冒泡阶段调用,个人认为处于目标阶段也会调用的
});
c.addEventListener("click", function (event) {
    console.log("c2");
}, true);
b.addEventListener("click", function (event) {
    console.log("b");
}, true);
a.addEventListener("click", function (event) {
    console.log("a1");
}, true);
a.addEventListener("click", function (event) {
    console.log("a2")
});
a.addEventListener("click", function (event) {
    console.log("a3");
    event.stopImmediatePropagation();
}, true);
a.addEventListener("click", function (event) {
    console.log("a4");
}, true);

那么现在有三个问题:

  • 如果点击c或者b,输出什么?(答案是 a1、a3

    stopImmediatePropagation包含了 stopPropagation的功能,即阻止事件传播(捕获或冒泡),但同时也阻止该元素上后来绑定的事件处理程序被调用,所以不输出 a4。因为事件捕获被拦截了,自然不会触发 b、c上的事件,所以不输出 b、c1、c2,冒泡更谈不上了,所以不输出 a2

  • 如果点击a,输出什么?(答案是 a1、a2、a3

    虽然这三个事件处理程序注册时指定了 true、false,但现在事件流是处于目标阶段,不是冒泡阶段、也不是捕获阶段,事件处理程序被调用的顺序是注册的顺序。不论你指定的是 true还是 false。换句话来说就是现在点击的是 a这个盒子本身,它处于事件流的目标状态,而既非冒泡,又非捕获。(需要注意的是,此时的 eventPhase为2,说明事件流处于目标阶段。当点击 a的时候,先从 document捕获,然后一步步往下找,找到 a这个元素的时候,此时的 targetcurrentTarget是一致的,所以认定到底了,不需要再捕获了,此时就按顺序执行已经预定的事件处理函数,执行完毕后再继续往上冒泡…)

  • 如果注释掉 event.stopImmediatePropagation,点击c,会输出什么?(答案是 a1、a3、a4、b、c1、c2、a2

    如果同一个事件处理程序(指针相同,比如用 handler保存的事件处理程序),用 addEventListenerattachEvent绑定多次,如果第三个参数是相同的话,也只会被调用一次。当然,如果第三个参数一个设置为 true,另一个设置为 false,那么会被调用两次。


Ajax


Asynchronous Javascript And XML的缩写,即异步 JavaScript 和 XML。

不考虑async等语法的前提下:

  • 异步本来就没返回值(或者说返回值不是想要的结果)。但异步操作一般都是和回调函数绑定使用的,所有依赖返回结果的方法都要在回调函数中进行。
  • 监听对象属性,本身也是个异步操作,只不过是将原本函数回调中的一些职责转移到了监听的回调里,因为你本来就可以在原本函数的回调中进行这些依赖返回值得函数的调用。
  • 同步ajax请求用的比较少。例如在做访问记录时,页面关闭前确保请求发送成功。
// 基础版
var xhr = new XMLHttpRequest();
xhr.open('get', '/api', false);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      console.log(xhr.responseText);
    }
  }
}
xhr.send(null);


/**
 * 封装一个get请求的ajax操作
 * 
 * @param {String} url 请求的地址
 * @returns Promise
 */
function getJSON(url) {
  return new Promise((resolve, reject) => {
    var XHR = new XMLHttpRequest();
    XHR.open('get', url, true);
    XHR.send();

    XHR.onreadystatechange = function () {
      if (XHR.readyState === 4) {
        if (XHR.status === 200) {
          // 成功
          try {
            var result = JSON.parse(XHR.responseText);
            resolve(result);
          } catch (e) {
            reject(e);
          }
        } else {
          // 失败
          reject(new Error(XHR.statusText));
        }
      }
    }
  })
}

// 使用
var url = './api.json';
getJSON(url).then((value) => {
  console.log(value);
}).catch((err) => {
  console.log(err);
})

readyState

readyState是XHR对象的一个属性,该属性表示请求/响应过程的当前活动阶段。该属性的取值如下:

  • 0:未初始化 尚未调用open()方法
  • 1:启动 已经调用open()方法,但尚未调用send()方法
  • 2:发送 已经调用send()方法,但尚未接收到响应
  • 3:接收 已经接收到部分响应数据
  • 4:完成 已经接收到全部响应数据,而且已经可以在客户端上使用了

只要readyState属性的值由一个值变成另一个值,都会触发一次readystatechange事件。


跨域

跨域是指一个域下的文档或者脚本视图去请求另一个域下的资源

  • 浏览器有同源策略,不允许ajax访问其他域的接口。

    • 同源策略限制一下的几种行为
      • Cookie,LocalStorage,IndexDB无法获取
      • DOM 和 JS 对象无法获得
      • AJAX请求不能发送
    • 可以跨域的三个标签
      • <img src=xxx>
      • <link href=xxx>
      • <script src=xxx>
      • 使用场景
        • <img>用于打点统计,统计网站可能是其他域
        • <link> <script>可以使用CDN,CDN也可能是其他域
        • <script>可以用于JSONP
    • 跨域注意事项
      • 所有的跨域请求都必须经过信息提供方允许
      • 如果未经允许即可获取,那是浏览器同源策略出现漏洞
  • 跨域条件:协议、域名、端口(有一个不同就算是跨域)

    • http的默认端口为80,https的默认端口为443

跨域常用的解决方案
  • 通过JSONP跨域(只能实现get请求):利用<script>可以跨域的特性

    // demo-1
    <script>
    	window.callback = function (data) {
    		// 这是跨域得到的信息
    		console.log(data)
    	}
    </script>
    
    <script src="http://imooc.com/api.js"></script>
    <!-- 以上将返回callback({x:100, y:200}) -->
    
    
    // demo-2
    <script>
        var script = document.createElement('script');
        script.type = 'text/javascript';
    
        // 传参并指定回调执行函数为onBack
        script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
        document.head.appendChild(script);
    
        // 回调执行函数
        function onBack(res) {
            alert(JSON.stringify(res));
        }
    </script>
    
    onBack({"status": true, "user": "admin"})
    
  • 跨域资源共享(CORS —— Cross-Origin Resource Sharing)

    • 前端需要设置请求头信息
      • 设置 Access-Control-Request-Method:必写,用来列出浏览器CORS请求可以使用的HTTP方法
      • 设置 Access-Control-Request-Headers:用来指定浏览器可额外发送的请求头
    • 后端需要设置 http header
      • 设置 Access-Control-Allow-Origin:必写,请求域的domin或者(*) ,*代表着允许所有域名的跨域请求
      • 设置 Access-Control-Allow-Headers:在CORS请求时浏览器的 XMLHttpRequest对象的 getResponseHeader()方法只可以拿到六个基本字段,如果还想拿到其他字段就必须在 Access-Control-Expose-Header中指定。
      • 设置 Access-Control-Allow-Credentials:可写,该值是一个布尔值,表示是否允许发送 Cookie,默认情况下,Cookie不包括 CORS请求中。
  • nginx反向代理

  • nodejs中间件代理跨域(vue开发环境下的proxy就是使用这种方式)

  • websocket协议跨域



存储


cookie

  • 本身用于客户端和服务端通信,但是它有本地存储的功能,所以就被“借用”。
  • 使用 document.cookie = ...获取和修改
  • cookie用于存储的缺点
    • 存储量太小。只有4KB
    • 所有http请求都带着,会影响获取资源的效率
    • API简单,需要封装才能用

localStorage 和 sessionStorage

  • HTML5专门为存储而设计的,最大容量为5M

  • http请求不会携带

  • API简单易用

    localStorage.setItem(key, value);
    localStorage.getItem(key);
    sessionStorage.setItem(key, value);
    sessionStorage.getItem(key);
    
  • 使用的一些坑

    • IOS safari 隐藏模式下,使用localStorage.getItem(key)会报错
    • 建议统一使用try-catch封装

cookie和sessionStorage,localStorage的区别

  • 容量
    • cookie只有4KB
    • sessionStorage,localStorage最大容量为5M
  • 是否会携带到http请求中
    • 所有http请求都会携带cookie
    • http请求不会携带sessionStorage,localStorage
  • API易用性
    • cookie的过于简单,需要进行封装才可以使用
    • sessionStorage,localStorage的API简单易用,无需封装

1. localStorage 是否可以代替 Cookie —— 为了防止通过 XSS获取 Cookie数据,浏览器支持了使用 HttpOnly来保护 Cookie不被 XSS攻击获取到。而 localStorage存储没有对 XSS攻击有任何的抵御机制。一旦出现 XSS漏洞,那么存储在 localStorage里的数据就极易被获取到。

2. 容易遭受跨目录攻击 —— localStorage存储方式不会像 Cookie存储一样可以指定域中的路径,在 localStorage存储方式中没有域路径的概念。也就是说,如果一个域下的任意路径存在 XSS漏洞,整个域下存储的数据,在知道存储名称的情况下,都可以被获取到。

3. 容易遭受DNS欺骗攻击

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值