前端工作室专注前端开发
 博客首页

《JavaScript高级程序设计(第3版)》10-23章总结

2021-03-16 09:14:01
JS基础与深入
28
文章图片

《JavaScript高级程序设计(第3版)》10-23章总结

第10章 DOM

DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)。 DOM描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。

10.1 节点层次

console.log(Node.prototype) //打印出来看看就好,别记
console.log(Document.prototype)
console.log(Element.prototype)
console.log(Text.prototype)
console.log(Comment.prototype)
console.log(DocumentType.prototype)
console.log(DocumentFragment.prototype)
console.log(CDATASection.prototype)

10.2 DOM操作技术

10.2.1 动态脚本

var script = document.createElement("script"); 
script.type = "text/javascript";
script.src = "client.js"; 
document.body.appendChild(script);

封装:

function loadScript(url){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}

10.2.2 动态样式

var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = "style.css";
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);

封装:

function loadStyles(url){
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.type = "text/css";
    link.href = url;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(link);
}

10.2.3 操作表格

10.2.4 使用NodeList

var divs = document.getElementsByTagName("div");
console.log(divs)

第11章 DOM扩展

11.1 选择符 API

Selectors API Level 1 的核心是两个方法:querySelector()和 querySelectorAll()。

11.1.1 querySelector()方法

//取得 body 元素
var body = document.querySelector("body"); 
//取得 ID 为"myDiv"的元素
var myDiv = document.querySelector("#myDiv"); 
//取得类为"selected"的第一个元素
var selected = document.querySelector(".selected"); 
//取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");

11.1.2 querySelectorAll()方法

//取得某<div>中的所有<em>元素(类似于 getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");
//取得类为"selected"的所有元素
var selecteds = document.querySelectorAll(".selected");
//取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");

11.1.3 matchesSelector()方法

11.2 元素遍历

var i,
    len,
    child = element.firstElementChild;
while(child != element.lastElementChild){
    processChild(child); //已知其是元素
    child = child.nextElementSibling;
}

11.3 HTML5

  1. 与类相关的扩充:增加getElementsByClassName()方法、classList 属性
    // 取得所有类中包含"username""current"的元素,类名的先后顺序无所谓
    document.getElementsByClassName("username current")
    var div = document.querySelector('.c-tips-container');
    div.classList.add('current') // 添加class
  2. 焦点管理:新增document.activeElement和document.hasFocus()
    var input = document.querySelector('#kw');
    input.focus();
    console.log(document.activeElement === input,document.hasFocus()) // true, false
    并没有像书上所说document.hasFocus()返回true。
  3. HTMLDocument的变化:引入 readyState 属性、兼容模式、head属性
    console.log(document.readyState) // "complete"(可以判断文档是否加载完毕,返回值有complete和loading)
    console.log(document.compatMode) // "CSS1Compat"(CSS1Compat表示标准模式,BackCompat表示混杂模式)
    console.log(document.head) // <head>...</head>
  4. 字符集属性
    console.log(document.charset) // "UTF-8"
  5. 自定义数据属性: data-
    ``` var div = document.getElementById("myDiv"); //取得自定义属性的值 var appId = div.dataset.appId; var myName = div.dataset.myname; //设置值 div.dataset.appId = 23456; div.dataset.myname = "Michael"; ```
  6. 插入标记:innerHTML属性和outerHTML 属性、insertAdjacentHTML()方法 7、滚动:scrollIntoView()方法
    //让元素可见
    document.forms[0].scrollIntoView();

第12章 DOM2 和 DOM3

12.1 DOM变化

12.2 样式

12.2.1 访问元素的样式

var myDiv = document.getElementById("su"); 
//设置背景颜色
myDiv.style.backgroundColor = "red"; 
//改变大小
myDiv.style.width = "100px"; 
myDiv.style.height = "200px"; 
//指定边框
myDiv.style.border = "1px solid black";
console.log(myDiv.style) // 略
console.log(myDiv.style.cssText) // 'background-color: red; width: 100px; height: 200px; border: 1px solid black;'

1、 DOM样式属性和方法 cssText: 访问style特性中的css代码 length: css属性数量 parentRule:cssRule对象 getPropertyCSSValue(propertyName): 返回属性值的CSSValue对象 //经我实验,已经删除该属性 getPropertyPriority(propertyName):设置了"!important"返回"import",否则返回空字符串。 getPropertyValue(propertyName):返回给定属性的字符串值。 item(index):返回给定位置的 CSS 属性的名称。 removeProperty(propertyName):从样式中删除给定属性。 setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志("important"或者一个空字符串)。

var myDiv = document.getElementById("su"); 
myDiv.style.color = "red";
myDiv.style.cssText = "width: 25px !important; height: 100px; background-color: green";
console.log(myDiv.style.cssText) // height: 100px; background-color: green; width: 25px !important;(会把原来属性覆盖,不建议使用,例如前面的color)
console.log(myDiv.style.length) // 3
console.log(myDiv.style.parentRule) // null
console.log(myDiv.style.getPropertyPriority("width")) // "important"
console.log(myDiv.style.getPropertyValue("width")) // "25px"
console.log(myDiv.style.item(1)) // "background-color"
myDiv.style.removeProperty("height")
console.log(myDiv.style.cssText) // "background-color: green; width: 25px !important;"height被删除了)
myDiv.style.setProperty("height","200px","important")
console.log(myDiv.style.cssText) // "background-color: green; width: 25px !important; height: 200px !important;"height被添加了)

2、计算的样式 document.defaultView.getComputedStyle():要取得计算样式的元素和一个伪元素字符串(例如":after")

var myDiv = document.getElementById("su"); 
var computedStyle = document.defaultView.getComputedStyle(myDiv, null);
console.log(computedStyle.width) // "100px"(返回层叠后的最终样式)
console.log(myDiv.style.width) // “25px"(返回style中的样式)

12.2.2 操作样式表

function getStyleSheet(element){ 
 return element.sheet || element.styleSheet; 
} 
//取得第一个<link/>元素引入的样式表
var link = document.getElementsByTagName("link")[0]; 
var sheet = getStyleSheet(link);
function insertRule(sheet, selectorText, cssText, position){ 
 if (sheet.insertRule){ 
 sheet.insertRule(selectorText + "{" + cssText + "}", position); 
 } else if (sheet.addRule){ 
 sheet.addRule(selectorText, cssText, position); 
 } 
}
//增加规则
insertRule(document.styleSheets[0], "body", "background-color: silver", 0);
function deleteRule(sheet, index){ 
 if (sheet.deleteRule){ 
 sheet.deleteRule(index); 
 } else if (sheet.removeRule){ 
 sheet.removeRule(index); 
 } 
}
//删除
deleteRule(document.styleSheets[0], 0);

12.2.3 元素大小

1、偏移量 offsetHeight、offsetWidth、offsetLeft、offsetTop

var myDiv = document.getElementById("su"); 
console.log(myDiv.offsetHeight) // 44(元素垂直方向占据空间大小,包括左右10px边框)
console.log(myDiv.offsetWidth) // 128(元素水平方向占据空间大小,包括左右10px边框)
console.log(myDiv.offsetLeft) // 0
console.log(myDiv.offsetTop) // 0

2、客户区大小 元素的客户区大小(client dimension),指的是元素内容及其内边距所占据的空间大小

var myDiv = document.getElementById("su"); 
console.log(myDiv.clientWidth) // 108(元素垂直方向占据空间大小,包括边框)
console.log(myDiv.clientHeight) // 44(元素水平方向占据空间大小,包括边框)

获取浏览器视口

function getViewport(){ 
 if (document.compatMode == "BackCompat"){ 
 return { 
 width: document.body.clientWidth, 
 height: document.body.clientHeight 
 }; 
 } else { 
 return { 
 width: document.documentElement.clientWidth, 
 height: document.documentElement.clientHeight 
 }; 
 } 
}
getViewport() // {width: 1090, height: 920}

3、滚动大小 scrollHeight、scrollWidth、scrollLeft、scrollTop

var myDiv = document.getElementById("sull"); 
console.log(myDiv.scrollLeft) // 32
console.log(myDiv.scrollHeight) // 64
console.log(myDiv.scrollWidth) // 128
console.log(myDiv.scrollTop) // 37
myDiv.scrollTop =0; // 滚动条移动到顶部

4、确定元素大小 略

12.3 遍历

12.3.1 NodeIterator

var div = document.getElementById("sull"); 
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, null, false); 
var node = iterator.nextNode(); 
while (node !== null) { 
 console.log(node.tagName); //输出标签名
 node = iterator.nextNode(); 
}
// 输出 SPAN INPUT

只遍历其中的li元素

var div = document.getElementById("form"); 
var filter = function(node){ 
 return node.tagName.toLowerCase() == "li" ? 
 NodeFilter.FILTER_ACCEPT : 
 NodeFilter.FILTER_SKIP; 
}; 
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT, 
 filter, false); 
var node = iterator.nextNode(); 
while (node !== null) { 
 console.log(node.tagName); //输出标签名
 node = iterator.nextNode(); 
}
// 输出 LI LI LI LI

12.3.2 TreeWalker

var div = document.getElementById("form"); 
var filter = function(node){ 
 return node.tagName.toLowerCase() == "li"? 
 NodeFilter.FILTER_ACCEPT : 
 NodeFilter.FILTER_SKIP; 
}; 
var iterator= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, 
 filter, false); 
var node = iterator.nextNode(); 
while (node !== null) { 
 console.log(node.tagName); //输出标签名
 node = iterator.nextNode(); 
}
// LI LI LI LI
var div = document.getElementById("head"); 
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false); 
walker.firstChild(); //第一个子元素
walker.nextSibling(); //下一个兄弟元素
var node = walker.firstChild(); //第一个子元素
while (node !== null) { 
 console.log(node.tagName); 
 node = walker.nextSibling(); 
}

12.4 范围

第13章 事件

13.1 事件流

13.1.1 事件冒泡

IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档)。从里面往外面冒泡,记忆IE处理事情总是冒冒失失的。直至传播到 document 对象

<!DOCTYPE html> 
<html>
<head> 
<meta charset="utf-8"> 
<title>测试</title> 
</head>
<body onclick="handleClick('2')" style="height: 100%; width: 100%; background-color: green">
  <div style="height: 100px; width: 100px; background-color: yellow" onclick="handleClick('1')">
    <div style="height: 50px; width: 50px; background-color: red" onclick="handleClick('0')"></div>
  </div>
</body>
<script>
  function handleClick(type){
    console.log(type)
  }
</script>
</html>

点击红色方块输出 0 1 2

13.1.2 事件捕获

在事件捕获过程中,document 对象首先接收到 click 事件,然后事件沿 DOM 树依次向下,一直传播到事件的实际目标,即

元素。 不建议使用

13.1.3 DOM事件流

先捕获(用于拦截事件),再冒泡

<!DOCTYPE html> 
<html>
<head> 
<meta charset="utf-8"> 
<title>测试</title> 
</head>
<body onclick="handleClick('2')" style="height: 100%; width: 100%; background-color: green">
  <div style="height: 100px; width: 100px; background-color: yellow" onclick="handleClick('1')">
    <div style="height: 50px; width: 50px; background-color: red" onclick="handleClick('0')"></div>
  </div>
</body>
<script>
  function handleClick(type){
    console.log(type)
  }
</script>
</html>

13.2 事件处理程序

事件处理程序的名字以"on"开头,因此click 事件的事件处理程序就是 onclick,load 事件的事件处理程序就是 onload。

13.2.1 HTML事件处理程序

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" onclick="showMessage()" />
    <input type="button" value="Click Me" onclick="console.log('Hello world!');" />
  </body>
  <script type="text/javascript">
    function showMessage(){
        console.log("Hello world!");
    }
  </script>
</html>
// 点击都输出  "hello world!"

13.2.2 DOM0 级事件处理程序

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" id="btn"/>
  </body>
  <script type="text/javascript">
    function showMessage(){
        console.log(this.id);
    }
    var btn = document.getElementById('btn');
    btn.onclick = showMessage;
  </script>
</html>
// 点击输出 "btn"

btn.onclick = null; //删除事件处理程序

13.2.3 DOM2 级事件处理程序

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" id="btn"/>
  </body>
  <script type="text/javascript">
    function showMessage(){
      console.log(this.id);
    }
    var btn = document.getElementById('btn');
    btn.addEventListener("click",showMessage,false) // 
  </script>
</html>
// 点击输出 "btn"

都接受 3 个参数:要处理的事件名、作为事件处理程序的函数和一个布尔值。最后这个布尔值参数如果是 true,表示在捕获阶段调用事件处理程序;如果是 false,表示在冒泡阶段调用事件处理程序。当然一般传false。 btn.removeEventListener("click",showMessage,false) // 移除监听 注意: 匿名函数无法移除监听

var btn = document.getElementById("myBtn");
btn.addEventListener("click", function(){
    console.log(this.id);
}, false);
//这里省略了其他代码
btn.removeEventListener("click", function(){ //没有用!
    console.log(this.id);
}, false);

经我测试ie8以下addEventListener不起作用。不过一般都不用兼容ie8了吧。

13.2.4 IE事件处理程序

IE总是特立独行,在此就略过了。

13.3 事件对象

在触发 DOM 上的某个事件时,会产生一个事件对象 event,这个对象中包含着所有与事件有关的信息。包括导致事件的元素、事件的类型以及其他与特定事件相关的信息。

13.3.1 DOM中的事件对象

兼容 DOM 的浏览器会将一个 event 对象传入到事件处理程序中。无论指定事件处理程序时使用什么方法(DOM0 级或 DOM2 级),都会传入 event 对象。

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" id="btn"/>
  </body>
  <script type="text/javascript">
    function show(){
      console.log(this.id);
    }
    var btn = document.getElementById("btn"); 
    btn.onclick = function(event){ 
      console.log(event.type); //"click" 
    }; 
    btn.addEventListener("click"function(event){ 
      console.log(event.type); //"click" 
    }, false);
  </script>
</html>

在事件处理程序内部,对象 this 始终等于 currentTarget 的值,而 target 则只包含事件的实际目标。如果直接将事件处理程序指定给了目标元素,则 this、currentTarget 和 target 包含相同的值。

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" id="btn"/>
  </body>
  <script type="text/javascript">
    function show(){
      console.log(this.id);
    }
    var btn = document.getElementById("btn"); 
    btn.onclick = function(event){ 
      console.log(event.currentTarget === this); //true 
      console.log(event.target === this); //true
    }; 
  </script>
</html>

如果把处理程序存于父节点中例如body上,则event.currentTarget和tartget不一样

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input type="button" value="Click Me" id="btn"/>
  </body>
  <script type="text/javascript">
    var btn = document.getElementById("btn"); 
    document.body.onclick = function(event){ 
      console.log(event.currentTarget === document.body); //true 
      console.log(this === document.body); //true 
      console.log(event.target === document.getElementById("btn")); //true 
    }; 
  </script>
</html>

阻止默认事件

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <a href="http://baidu.com" id="myLink">点我去百度</a>
  </body>
  <script type="text/javascript">
    var link = document.getElementById("myLink"); 
    link.onclick = function(event){ 
      event.preventDefault();  // 成功阻止了跳转
    };
  </script>
</html>

阻止冒泡

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input value="点击" id="btn" type="button"/>
  </body>
  <script type="text/javascript">
    var btn = document.getElementById("btn"); 
    btn.onclick = function(event){ 
      event.stopPropagation();
      console.log("取消冒泡")
    };
    document.body.onclick = function (){
      console.log('来不到这里了')
    }
  </script>
</html>

判断事件的阶段

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input value="点击" id="btn" type="button"/>
  </body>
  <script type="text/javascript">
    var btn = document.getElementById("btn");  
    btn.onclick = function(event){ 
      console.log(event.eventPhase); //2 
    }; 
    document.body.addEventListener("click"function(event){ 
      console.log(event.eventPhase); //1 
    }, true); 
    document.body.addEventListener("click"function(event){ 
      console.log(event.eventPhase); //3 
    }, false);
    document.body.onclick = function(event){ 
      console.log(event.eventPhase); //3 
    };
  </script>
</html>

13.4 事件类型

  1. UI(User Interface,用户界面)事件,当用户与页面上的元素交互时触发;
  2. 焦点事件,当元素获得或失去焦点时触发;
  3. 鼠标事件,当用户通过鼠标在页面上执行操作时触发;
  4. 滚轮事件,当使用鼠标滚轮(或类似设备)时触发;
  5. 文本事件,当在文档中输入文本时触发;
  6. 键盘事件,当用户通过键盘在页面上执行操作时触发;
  7. 合成事件,当为 IME(Input Method Editor,输入法编辑器)输入字符时触发;
  8. 变动(mutation)事件,当底层 DOM 结构发生变化时触发。 略

事件委托

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <ul onclick="handler(event)">
      <li id="1">1</li>
      <li id="2">2</li>
      <li id="3">3</li>
      <li id="4">4</li>
      <li id="5">5</li>
      <li id="6">6</li>
    </ul>
  </body>
  <script type="text/javascript">
    function handler(event){
      var target = event.target // 这里保存着点击元素的信息,例如可以访问id,这样可以减少事件的绑定
      console.log(target.id)
    }
  </script>
</html>

btn.onclick = null; //移除事件处理程序

模拟点击事件

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <input value="点击" id="btn" type="button"/>
  </body>
  <script type="text/javascript">
    var fn = function(){
      console.log('button was clicked'); // 此函数通过事件的方式被调用
    }
    var btn = document.getElementById('btn');
    btn.addEventListener('click', fn);
    // 点击事件 MouseEvent
    var mouseClick = document.createEvent('MouseEvent');
    mouseClick.initMouseEvent('click'falsefalsenull);
    // 执行
    btn.dispatchEvent(mouseClick); // 'click'
  </script>
</html>

第14章 表单脚本

action:接受请求的 URL;等价于 HTML 中的 action 特性。 elements:表单中所有控件的集合(HTMLCollection)。 length:表单中控件的数量。 method:要发送的 HTTP 请求类型,通常是"get"或"post";等价于 HTML 的 method 特性。 name:表单的名称;等价于 HTML 的 name 特性。 reset():将所有表单域重置为默认值。 submit():提交表单。 target:用于发送请求和接收响应的窗口名称;等价于 HTML 的 target 特性。

var form = document.getElementById("form");
console.log(document.forms[0] == form) // true
console.log(document.forms['form'] == form) // true

可以通过document.forms索引或name来获取特定表单

14.1.1 提交表单

<!-- 通用提交按钮 -->
<input type="submit" value="Submit Form">
<!-- 自定义提交按钮 -->
<button type="submit">Submit Form</button>

禁止表单提交

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <form method="get" name='form' action="https://www.baidu.com" id="form"><input type="text" name="name"/><button type="submit">提交</button></form>
  </body>
  <script type="text/javascript">
    var form = document.getElementById('form')
    form.onsubmit = function(event){
      event.preventDefault() // 禁止默认事件或者,return false
      //return false
    }
  </script>
</html>

手动提交表单


<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <form method="get" name='form' action="https://www.baidu.com" id="form"><input type="text" name="name"/><button type="submit">提交</button></form>
  </body>
  <script type="text/javascript">
    var form = document.getElementById('form')
    form.onsubmit = function(event){
      alert('进来了') //没有alert不会触发onsubmit事件
    }
    form.submit()
  </script>
</html>

14.1.2 重置表单

<!-- 通用重置按钮 -->
<input type="reset" value="Reset Form">
<!-- 自定义重置按钮 -->
<button type="reset">Reset Form</button>
<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <form method="get" name='form' action="https://www.baidu.com" id="form"><input type="text" name="name"/><button type="submit">提交</button></form>
  </body>
  <script type="text/javascript">
    var form = document.getElementById("form");
    form.onreset = function(){
      alert('重置了') //弹出alert
    }
    //重置表单
    form.reset();
  </script>
</html>

会触发onreset函数

14.1.3 访问表单字段

console.log(form.elements['name']) //输出 <input type="text" name="name"/>
console.log(form.elements['name'].value) // 输出你输入的值

14.2 文本框脚本

14.2.1 选择文本

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <textarea rows="25" cols="25" id="textarea"></textarea>
  </body>
  <script type="text/javascript">
    function getSelectedText(textbox){ 
      return textbox.value.substring(textbox.selectionStart, textbox.selectionEnd);  // 获取选中的字
    }
    var textarea = document.getElementById('textarea');
    textarea.onselect = function(){
      console.log(getSelectedText(textarea)) // 输出选中的字
    }
  </script>
</html>

14.2.2 过滤输入

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <textarea rows="25" cols="25" id="textarea"></textarea>
  </body>
  <script type="text/javascript">
    var textarea = document.getElementById('textarea');
    textarea.onkeypress = function(event){
      console.log(/[\d.]/.test(String.fromCharCode(event.keyCode)))
      if(!/\d/.test(String.fromCharCode(event.keyCode))){
        event.preventDefault()
      }
    }
  </script>
</html>

过滤非数字字符

14.2.3 焦点

var textbox1 = document.getElementById("txtTel1");
textbox1.focus()

14.2.4 HTML5 约束验证API

增加类型和必填检测

<input type="email" name="username" required>

数值范围

<input type="number" min="0" max="100" step="5" name="count">

输入模式

<input type="text" pattern="\d+" name="count">

检测有效性:使用 checkValidity() 对表单内某个元素使用

if (document.forms[0].elements[0].checkValidity()){
//字段有效,继续
} else {
//字段无效
}

对整个表单使用

if(document.forms[0].checkValidity()){
//表单有效,继续
} else {
//表单无效
}

例子:

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <form onsubmit="handleSumbit(event)" name="form" id="form">
      <input type="text" pattern="\d+" name="count">
      <input type="email" name="email">
      <button type="submit">提交</button>
    </form>
  </body>
  <script type="text/javascript">
    function handleSumbit(event){
      event.preventDefault()
      console.log(document.forms['form'].elements['count'].checkValidity())
      console.log(document.forms['form'].checkValidity())
    }
  </script>
</html>

输入数字或者不输入点击时两个都返回true 禁用验证

<form method="post" action="signup.php" novalidate>
<!--这里插入表单元素-->
</form>

14.3 选择框脚本

14.3.1 选择选项

var selectedOption = selectbox.options[selectbox.selectedIndex];

文本selectedOption.text 值selectedOption.value 设置选项为true selectbox.options[0].selected = true;

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <select id="selectbox"> 
    <option value="1">one</option> 
    <option value="2">two</option> 
    <option value="3">three</option> 
    </select>
  </body>
  <script type="text/javascript">
    var selectbox = document.getElementById('selectbox');
    selectbox.options[0].selected = true;
    var selectedOption = selectbox.options[selectbox.selectedIndex];
    console.log(selectedOption.text,selectedOption.value) // "one" "1"
    selectbox.options[1].selected = true;
    var selectedOption = selectbox.options[selectbox.selectedIndex];
    console.log(selectedOption.text,selectedOption.value) // "two" "2"
  </script>
</html>

单选时js代码重新选中时,会导致原来的默认被取消,不需要手动取消。 多选时,需要遍历输出每一项

<!DOCTYPE html> 
<html>
  <head> 
    <meta charset="utf-8"> 
    <title>测试</title> 
  </head>
  <body>
    <select id="selectbox" multiple> 
    <option value="1">one</option> 
    <option value="2">two</option> 
    <option value="3">three</option> 
    </select>
  </body>
  <script type="text/javascript">
    var selectbox1 = document.getElementById('selectbox');
    selectbox1.options[0].selected = true;
    selectbox1.options[1].selected = true;
    function getSelectedOptions(selectbox){ 
      var result = new Array(); 
      var option = null; 
      for (var i=0, len=selectbox.options.length; i < len; i++){ 
        option = selectbox.options[i]; 
        if (option.selected){ 
          result.push(option); 
        } 
      } 
      return result; 
    }
    console.log(getSelectedOptions(selectbox1))
  </script>
</html>

14.3.2 添加选项

var newOption = document.createElement("option");
newOption.appendChild(document.createTextNode("Option text"));
newOption.setAttribute("value", "Option value");
selectbox.appendChild(newOption);
var newOption = new Option("Option text", "Option value"); 
selectbox.add(newOption, undefined); //最佳方案

14.3.3 移除选项

selectbox.removeChild(selectbox.options[0]);
selectbox.remove(0); //移除第一个选项
selectbox.options[0] = null; //移除第一个选项

14.3.4 移动和重排

选项复制到另一个选中框

var selectbox1 = document.getElementById("selLocations1");
var selectbox2 = document.getElementById("selLocations2");
selectbox2.appendChild(selectbox1.options[0]);
var optionToMove = selectbox.options[1]; 
selectbox.insertBefore(optionToMove, selectbox.options[optionToMove.index+2]); //+2才向后移动一个位置

14.4 表单序列

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>测试</title>
</head>
<body>
  <form id="form">
    <input type="text" name="input"/>
    <select id="selectbox" multiple name="select">
    <option value="1">one</option>
    <option value="2">two</option>
    <option value="3">three</option>
    </select>
    <input type="radio" name="sex" value="1" checked="checked">男
    <input type="radio" name="sex" value="2">女
    <input type="checkbox" name="vote">同意
    <button type="submit">提交</button>
  </form>
</body>
<script type="text/javascript">
  function serialize(form{
    var parts = [],
      field = null,
      i,
      len,
      j,
      optLen,
      option,
      optValue;
    for (i = 0, len = form.elements.length; i < len; i++) {
      field = form.elements[i];
      switch (field.type) {
        case "select-one":
        case "select-multiple":
          if (field.name.length) {
            for (j = 0, optLen = field.options.length; j < optLen; j++) {
              option = field.options[j];
              if (option.selected) {
                optValue = "";
                if (option.hasAttribute) {
                  optValue = (option.hasAttribute("value") ?
                    option.value : option.text);
                } else {
                  optValue = (option.attributes["value"].specified ?
                    option.value : option.text);
                }
                parts.push(encodeURIComponent(field.name) + "=" +
                  encodeURIComponent(optValue));
              }
            }
          }
          break;
        case undefined//字段集
        case "file"//文件输入
        case "submit"//提交按钮
        case "reset"//重置按钮
        case "button"//自定义按钮
          break;
        case "radio"//单选按钮
        case "checkbox"//复选框
          if (!field.checked) {
            break;
          }
        /* 执行默认操作 */
        default:
          //不包含没有名字的表单字段
          if (field.name.length) {
            parts.push(encodeURIComponent(field.name) + "=" +
              encodeURIComponent(field.value));
          }
      }
    }
    return parts.join("&");
  }
  var form1 = document.getElementById('form')
  form1.onsubmit = function(event){
    event.preventDefault();
    console.log(serialize(form1)) // 'input=fdfdd&select=3&sex=2&vote=on'(序列化了)
  }
</script>
</html>

第20章 JSON

20.1 语法

JSON语法可以表示以下类型的值: 简单值、对象、数组。

20.1.1 简单值

JSON表示数值5: 5 JSON表示字符串的方式:‘lzw’

20.1.2 对象

{
    "name": "Nicholas",
    "age": 29,
    "school": {
        "name": "Merrimack College",
        "location": "North Andover, MA"
    }
}

20.1.3 数组

[
    {
        "title": "Professional JavaScript",
        "authors": [
            "Nicholas C. Zakas"
        ],
        edition: 3,
        year: 2011
    },
    {
        "title": "Professional JavaScript",
        "authors": [
            "Nicholas C. Zakas"
        ],
        edition: 2,
        year: 2009
    }
]

20.2 解析与序列化

20.2.1 JSON对象

var book = { 
 title: "Professional JavaScript", 
 authors: [ 
 "Nicholas C. Zakas" 
 ], 
 edition: 3, 
 year: 2011 
 }; 
var jsonText = JSON.stringify(book);
console.log(jsonText) // {"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011}
var bookCopy = JSON.parse(jsonText);
console.log(bookCopy) // {title: "Professional JavaScript", authors: Array(1), edition: 3, year: 2011}

可用于深拷贝对象 值为undefined的属性会被忽略

var book = { 
 title: "Professional JavaScript", 
 authors: [ 
 "Nicholas C. Zakas" 
 ], 
 edition: 3, 
 year: 2011,
 price: undefined,
 storage: null
 }; 
var jsonText = JSON.stringify(book);
console.log(jsonText) // {"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011,"storage":null}(undefined属性值消失了)

20.2.2 序列化选项

1、过滤结果

var book = { 
 "title": "Professional JavaScript", 
 "authors": [ 
 "Nicholas C. Zakas" 
 ], 
 edition: 3, 
 year: 2011 
 }; 
var jsonText = JSON.stringify(book, ["title", "edition"]);
console.log(jsonText) // {"title":"Professional JavaScript","edition":3}(只输出数组里含有的属性)

第二个参数为函数时

var book = { 
 "title": "Professional JavaScript", 
 "authors": [ 
 "Nicholas C. Zakas" 
 ], 
 edition: 3, 
 year: 2011 
 }; 
var jsonText = JSON.stringify(book, function(key, value){ 
 switch(key){ 
 case "authors": 
 return value.join(",") 
 case "year": 
 return 5000; 
 case "edition": 
 return undefined; 
 default: 
 return value; 
 } 
});
console.log(jsonText) // {"title":"Professional JavaScript","authors":"Nicholas C. Zakas","year":5000} (undefined依然会被忽略)

toJson方法

var book = { 
 "title": "Professional JavaScript", 
 "authors": [ 
 "Nicholas C. Zakas" 
 ], 
 edition: 3, 
 year: 2011, 
 toJSON: function(){
    return this.title; 
 } 
 }; 
var jsonText = JSON.stringify(book);
console.log(jsonText) // "Professional JavaScript"

20.2.3 解析选项

JOSON.parse()

var book = { 
     "title": "Professional JavaScript", 
     "authors": [ 
        "Nicholas C. Zakas" 
     ], 
     edition: 3, 
     year: 2011, 
     releaseDate: new Date(2011, 11, 1) 
 }; 
var jsonText = JSON.stringify(book); 
var bookCopy = JSON.parse(jsonText, function(key, value){ 
 if (key == "releaseDate"){ 
    return new Date(value); 
 } else { 
     return value; 
 }
}); 
console.log(jsonText) // {"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,"year":2011,"releaseDate":"2011-11-30T16:00:00.000Z"}
console.log(bookCopy) // {title: "Professional JavaScript", authors: Array(1), edition: 3, year: 2011, releaseDate: Thu Dec 01 2011 00:00:00 GMT+0800 (中国标准时间)}

第21章 Ajax 与 Comet

21.1 XMLHttpRequest对象

function createXHR(){ 
    if (typeof XMLHttpRequest != "undefined"){ 
        return new XMLHttpRequest(); 
    } else if (typeof ActiveXObject != "undefined"){ 
        if (typeof arguments.callee.activeXString != "string"){ 
            var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 
            "MSXML2.XMLHttp"], 
            i, len; 
            for (i=0,len=versions.length; i < len; i++){ 
                try { 
                    new ActiveXObject(versions[i]); 
                    arguments.callee.activeXString = versions[i]; 
                    break; 
                } catch (ex){ 
                    //跳过
                } 
            } 
        } 
        return new ActiveXObject(arguments.callee.activeXString); 
    } else { 
        throw new Error("No XHR object available."); 
    } 
}
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
        console.log(xhr.responseText);
    } else {
        console.log("Request was unsuccessful: " + xhr.status);
    }
    }
};
xhr.open("get", "example.txt", true);
xhr.send(null);

21.1.1XHR的用法

在使用 XHR 对象时,要调用的第一个方法是 open(),它接受 3 个参数:要发送的请求的类型("get"、"post"等)、请求的 URL 和表示是否异步发送请求的布尔值。 要发送特定的请求,必须像下面这样调用 send()方法:

xhr.open("get", "example.txt", false);
xhr.send(null);

由于这次请求是同步的,JavaScript 代码会等到服务器响应之后再继续执行。 XHR 对象的属性  responseText:作为响应主体被返回的文本。  responseXML:如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保 存包含着响应数据的 XML DOM 文档。  status:响应的 HTTP 状态。  statusText:HTTP 状态的说明。 XHR 对象的 readyState 属性  0:未初始化。尚未调用 open()方法。  1:启动。已经调用 open()方法,但尚未调用 send()方法。  2:发送。已经调用 send()方法,但尚未接收到响应。  3:接收。已经接收到部分响应数据。  4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了。

xhr.abort();//停止请求

21.1.2 HTTP头部信息

 Accept:浏览器能够处理的内容类型。  Accept-Charset:浏览器能够显示的字符集。  Accept-Encoding:浏览器能够处理的压缩编码。  Accept-Language:浏览器当前设置的语言。  Connection:浏览器与服务器之间连接的类型。  Cookie:当前页面设置的任何 Cookie。  Host:发出请求的页面所在的域 。  Referer:发出请求的页面的 URI。注意,HTTP 规范将这个头部字段拼写错了,而为保证与规 范一致,也只能将错就错了。(这个英文单词的正确拼法应该是 referrer。)  User-Agent:浏览器的用户代理字符串。

xhr.setRequestHeader("MyHeader", "MyValue"); //自定义头部

21.1.3 GET请求

添加参数

function addURLParam(url, name, value) {
    url += (url.indexOf("?") == -1 ? "?" : "&");
    url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
    return url;
}
var url = "example.php";
//添加参数
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
//初始化请求
xhr.open("get", url, false);

21.1.4 POST请求

function submitData(){
    var xhr = createXHR();
    xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
            alert(xhr.responseText);
        } else {
            alert("Request was unsuccessful: " + xhr.status);
        }
    }
    };
    xhr.open("post", "postexample.php", true);
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    var form = document.getElementById("user-info");
    xhr.send(serialize(form));
}

21.2.1 FormData

var data = new FormData();
data.append("name", "Nicholas"); //添加数据
var form = document.getElementById("user-info");
xhr.send(new FormData(form)); // 发送数据

21.3.2 progress事件

xhr.onprogress = function(event){
    var divStatus = document.getElementById("status");
    if (event.lengthComputable){
        divStatus.innerHTML = "Received " + event.position + " of " +
        event.totalSize +" bytes";
    }
};

21.4跨域共享资源

跨浏览器的CORS

function createCORSRequest(method, url){ 
    var xhr = new XMLHttpRequest(); 
    if ("withCredentials" in xhr){ 
        xhr.open(method, url, true); 
    } else if (typeof XDomainRequest != "undefined"){ 
        xhr = new XDomainRequest(); 
        xhr.open(method, url); } else { 
        xhr = null; 
    } 
    return xhr; 
} 
var request = createCORSRequest("get", "https://www.somewhere-else.com/page/"); 
if (request){ 
    request.onload = function(){ 
    //对 request.responseText 进行处理
    }; 
    request.send(); 
}

21.5 其他跨域技术

图像ping

var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas"; // 浏览器服务器单向通信

JSONP

function handleResponse(response){
    alert("You’re at IP address " + response.ip + ", which is in " +
    response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

第22章 高级技巧

22.1 高级函数

22.1.1安全的类型检测

console.log(Object.prototype.toString.call([])); //"[object Array]"
console.log(Object.prototype.toString.call('')); // "[object String]"
console.log(Object.prototype.toString.call(/l/)); // "[object RegExp]"

区分原生JSON对象

var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) == "[object JSON]";

22.1.2 作用域安全的构造函数

function Person(name, age, job){ 
     if (this instanceof Person){ 
         this.name = name; 
         this.age = age; 
        this.job = job; 
     } else { 
        return new Person(name, age, job); 
     } 
} 
var person1 = Person("Nicholas", 29, "Software Engineer"); 
console.log(window.name); //""(防止误操作导致往全局添加属性)
console.log(person1.name); //"Nicholas"

作用域安全带来的问题:

function Polygon(sides){
    if (this instanceof Polygon) {
        this.sides = sides;
        this.getArea = function(){
        return 0;
        };
    } else {
        return new Polygon(sides);
    }
}
function Rectangle(width, height){
    Polygon.call(this, 2);
    this.width = width;
    this.height = height;
    this.getArea = function(){
        return this.width * this.height;
    };
}
Rectangle.prototype = new Polygon(); // 去除该句就没有sides属性,因为this 对象并非 Polygon 的实例
var rect = new Rectangle(5, 1);
console.log(rect.sides); //2

22.1.3 惰性载入函数

方法一:调用的时候重写,重写后在调用,仅在第一次执行if语句

function createXHR(){ 
    if (typeof XMLHttpRequest != "undefined"){ 
        createXHR = function(){ 
            return new XMLHttpRequest(); 
        }; 
    } else if (typeof ActiveXObject != "undefined"){ 
        createXHR = function(){ 
            if (typeof arguments.callee.activeXString != "string"){ 
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 
                "MSXML2.XMLHttp"], 
                i, len; 
                for (i=0,len=versions.length; i < len; i++){ 
                    try { 
                    new ActiveXObject(versions[i]); 
                    arguments.callee.activeXString = versions[i]; 
                    break; 
                    } catch (ex){ 
                    //skip 
                    } 
                } 
            } 
            return new ActiveXObject(arguments.callee.activeXString); 
        }; 
    } else { 
        createXHR = function(){ 
            throw new Error("No XHR object available."); 
        }; 
    } 
    return createXHR(); 
}

方法二: 自执行函数返回适当函数

var createXHR = (function(){ 
    if (typeof XMLHttpRequest != "undefined"){ 
        return function(){ 
        return new XMLHttpRequest(); 
        }; 
    } else if (typeof ActiveXObject != "undefined"){ 
        return function(){ 
            if (typeof arguments.callee.activeXString != "string"){ 
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", 
                "MSXML2.XMLHttp"], 
                i, len; 
                for (i=0,len=versions.length; i < len; i++){ 
                    try { 
                    new ActiveXObject(versions[i]); 
                    arguments.callee.activeXString = versions[i]; 
                    break; 
                    } catch (ex){ 
                    //skip 
                    } 
                } 
            } 
            return new ActiveXObject(arguments.callee.activeXString); 
        }; 
    } else { 
        return function(){ 
            throw new Error("No XHR object available."); 
        }; 
    } 
})();

22.1.5 函数柯里化

与函数绑定紧密相关的主题是函数柯里化(function currying),它用于创建已经设置好了一个或多个参数的函数。

function add(num1, num2){ 
 return num1 + num2; 
} 
function curriedAdd(num2){ 
 return add(5, num2);  // 指定了第一个参数
} 
console.log(add(2, 3)); //5
console.log(curriedAdd(3)); //8
function curry(fn){ 
    var args = Array.prototype.slice.call(arguments, 1); 
    console.log(args) // [5, 12]
    return function(){ // 第二次执行这里,即调用curriedAdd时
        var innerArgs = Array.prototype.slice.call(arguments); 
        console.log(innerArgs); // [3]
        var finalArgs = args.concat(innerArgs); 
        console.log(finalArgs) // [5, 12, 3]
        return fn.apply(null, finalArgs); 
    }; 
}
function add(num1, num2){ 
 return num1 + num2; 
} 
var curriedAdd = curry(add, 5, 12); 
console.log(curriedAdd(3)); //20

22.2 防篡改对象

22.2.1 不可扩展对象

var person = { name: "Nicholas" }; 
Object.preventExtensions(person); 
person.age = 29; 
console.log(person.age); //undefined(不可拓展了)
console.log(Object.isExtensible(person)); //false(判断是否可以拓展)

严格模式下

"use strict"
var person = { name: "Nicholas" }; 
Object.preventExtensions(person); 
person.age = 29;  // 报错Uncaught TypeError: Cannot add property age, object is not extensible
console.log(person.age); 

22.2.2 密封的对象

密封对象不可扩展,而且已有成员的[[Configurable]]特性将被设置为 false。这就意味着不能删除属性和方法,因为不能使用 Object.defineProperty()把数据属性修改为访问器属性,或者相反。但是可以修改。

var person = { name: "Nicholas" }; 
Object.seal(person); 
person.age = 29; 
console.log(person.age); //undefined(无法添加)
delete person.name; 
console.log(person.name); //"Nicholas"(无法删除)
console.log(Object.isExtensible(person)); //false(不可拓展)
console.log(Object.isSealed(person)); //true(已经密封)
person.name = 'lzw';
console.log(person) // {name: "lzw"}

22.2.3 冻结的对象

最严格的防篡改级别是冻结对象(frozen object)。冻结的对象既不可扩展,又是密封的,而且对象数据属性的[[Writable]]特性会被设置为 false。如果定义[[Set]]函数,访问器属性仍然是可写的。

var person = { name: "Nicholas" }; 
Object.freeze(person); 
person.age = 29; 
console.log(person.age); //undefined
delete person.name; 
console.log(person.name); //"Nicholas"
person.name = "Greg"; 
console.log(person.name); //"Nicholas"(不能重写了)
console.log(Object.isExtensible(person)); //false
console.log(Object.isSealed(person)); //true
console.log(Object.isFrozen(person)); //true

22.3.3 函数节流

基本形式:

var processor = { 
    timeoutId: null, 
    //实际进行处理的方法
    performProcessing: function(){ 
    //实际执行的代码
    }, 
    //初始处理调用的方法
    process: function(){ 
        clearTimeout(this.timeoutId); 
        var that = this; 
        this.timeoutId = setTimeout(function(){ 
            that.performProcessing(); 
        }, 100); 
    } 
}; 
//尝试开始执行 
processor.process();

使用throttle函数简化:接收两个参数要执行的函数以及在哪个作用域中执行

function throttle(method, context) { 
    clearTimeout(method.tId); 
    method.tId= setTimeout(function(){ 
        method.call(context); 
    }, 100); 
}

如果没有给出第二个参数,那么就在全局作用域内执行该方法。

22.4 自定义事件

观察者模式由两类对象组成:主体和观察者。主体负责发布事件,同时观察者通过订阅这些事件来观察该主体。该模式的一个关键概念是主体并不知道观察者的任何事情,也就是说它可以独自存在并正常运作即使观察者不存在。

function EventTarget(){ 
    this.handlers = {}; 
} 
EventTarget.prototype = { 
    constructor: EventTarget, // 保持构造函数的正确指向
    addHandler: function(type, handler){ 
        if (typeof this.handlers[type] == "undefined"){ 
            this.handlers[type] = []; 
        } 
        this.handlers[type].push(handler); // 把每类事件对应的需要执行的函数全部存在数组中
    }, 
    fire: function(event){ 
        if (!event.target){ 
            event.target = this; 
        } 
        if (this.handlers[event.type] instanceof Array){ 
            var handlers = this.handlers[event.type]; // 获取存储起来的数组,该数组里面存放着需要执行的一堆函数
            for (var i=0, len=handlers.length; i < len; i++){ 
                handlers[i](event); // 把存储起来的函数全部遍历执行一遍
            } 
        } 
    }, 
    removeHandler: function(type, handler){ 
        if (this.handlers[type] instanceof Array){ 
            var handlers = this.handlers[type]; 
            for (var i=0, len=handlers.length; i < len; i++){ 
                if (handlers[i] === handler){ 
                    break; 
                } 
            } 
            handlers.splice(i, 1);// 删除其中某一个函数
        } 
    } 
};

第23章 离线应用与客户端存储

23.3 数据存储

cookies、sessionStorage、localStorage  clear(): 删除所有值;Firefox 中没有实现 。  getItem(name):根据指定的名字 name 获取对应的值。  key(index):获得 index 位置处的值的名字。  removeItem(name):删除由 name 指定的名值对儿。  setItem(name, value):为指定的 name 设置一个对应的值。