# day01 - Web APIs
學習目標:
能夠通過ID來獲取元素
能夠通過標簽名來獲取元素
能夠通過class來獲取元素
能夠通過選擇器來獲取元素
能夠獲取body和html元素
能夠給元素注冊事件
能夠修改元素的內容
能夠區分innerText和innerHTML的區別
能夠修改像div這類普通元素的屬性
能夠修改表單元素的屬性
能夠修改元素的樣式屬性
1.1. Web API介紹
1.1.1 API的概念
API(Application Programming Interface,應用程序編程接口)是一些預先定義的函數,目的是提供應用程序與開發人員基於某軟件或硬件得以訪問一組例程的能力,而又無需訪問源碼,無需理解其內部工作機制細節,只需直接調用使用即可。
舉例解釋什么是API。
例如,
C語言中有一個函數 fopen()可以打開硬盤上的文件,這個函數對於我們來說,就是一個C語言提供的打開文件的工具。
javascript中有一個函數alert()可以在頁面彈一個提示框,這個函數就是js提供的一個彈框工具。
這些工具(函數)由編程語言提供,內部的實現已經封裝好了,我們只要學會靈活的使用這些工具即可。
1.1.2 Web API的概念
Web API 是瀏覽器提供的一套操作瀏覽器功能和頁面元素的 API ( BOM 和 DOM )。
現階段我們主要針對於瀏覽器講解常用的 API , 主要針對瀏覽器做交互效果。比如我們想要瀏覽器彈出一個警示框, 直接使用 alert(‘彈出’)
MDN 詳細 API : https://developer.mozilla.org/zh-CN/docs/Web/API
因為 Web API 很多,所以我們將這個階段稱為 Web APIs。
此處的 Web API 特指瀏覽器提供的一系列API(很多函數或對象方法),即操作網頁的一系列工具。例如:操作html標簽、操作頁面地址的方法。
1.1.3 API 和 Web API 總結
-
API 是為我們程序員提供的一個接口,幫助我們實現某種功能,我們會使用就可以了,不必糾結內部如何實現
-
Web API 主要是針對於瀏覽器提供的接口,主要針對於瀏覽器做交互效果。
-
Web API 一般都有輸入和輸出(函數的傳參和返回值),Web API 很多都是方法(函數)
-
學習 Web API 可以結合前面學習內置對象方法的思路學習
1.2. DOM 介紹
1.2.1 什么是DOM
文檔對象模型(Document Object Model,簡稱DOM),是 W3C 組織推薦的處理可擴展標記語言(html或者xhtml)的標准編程接口。
W3C 已經定義了一系列的 DOM 接口,通過這些 DOM 接口可以改變網頁的內容、結構和樣式。
DOM是W3C組織制定的一套處理 html和xml文檔的規范,所有的瀏覽器都遵循了這套標准。
1.2.2. DOM樹
DOM樹 又稱為文檔樹模型,把文檔映射成樹形結構,通過節點對象對其處理,處理的結果可以加入到當前的頁面。
- 文檔:一個頁面就是一個文檔,DOM中使用document表示
- 節點:網頁中的所有內容,在文檔樹中都是節點(標簽、屬性、文本、注釋等),使用node表示
- 標簽節點:網頁中的所有標簽,通常稱為元素節點,又簡稱為“元素”,使用element表示
1.3. 獲取元素
為什么要獲取頁面元素?
例如:我們想要操作頁面上的某部分(顯示/隱藏,動畫),需要先獲取到該部分對應的元素,再對其進行操作。
1.3.1. 根據ID獲取
語法:document.getElementById(id)
作用:根據ID獲取元素對象
參數:id值,區分大小寫的字符串
返回值:元素對象 或 null
案例代碼
<body>
<div id="time">2019-9-9</div>
<script>
// 因為我們文檔頁面從上往下加載,所以先得有標簽 所以我們script寫到標簽的下面
var timer = document.getElementById('time');
console.log(timer);
console.log(typeof timer);
// console.dir 打印我們返回的元素對象 更好的查看里面的屬性和方法
console.dir(timer);
</script>
</body>
1.3.2. 根據標簽名獲取元素
語法:document.getElementsByTagName('標簽名') 或者 element.getElementsByTagName('標簽名')
作用:根據標簽名獲取元素對象
參數:標簽名
返回值:元素對象集合(偽數組,數組元素是元素對象)
案例代碼
<body>
<ul>
<li>知否知否,應是等你好久11</li>
<li>知否知否,應是等你好久22</li>
<li>知否知否,應是等你好久33</li>
<li>知否知否,應是等你好久44</li>
<li>知否知否,應是等你好久55</li>
</ul>
<ul id="nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
// 1.返回的是 獲取過來元素對象的集合 以偽數組的形式存儲的
var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2. 我們想要依次打印里面的元素對象我們可以采取遍歷的方式
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3. element.getElementsByTagName() 可以得到這個元素里面的某些標簽
var nav = document.getElementById('nav'); // 這個獲得nav 元素
var navLis = nav.getElementsByTagName('li');
console.log(navLis);
</script>
</body>
注意:getElementsByTagName()獲取到是動態集合,即:當頁面增加了標簽,這個集合中也就增加了元素。
1.3.3. H5新增獲取元素方式
案例代碼
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div id="nav">
<ul>
<li>首頁</li>
<li>產品</li>
</ul>
</div>
<script>
// 1. getElementsByClassName 根據類名獲得某些元素集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
// 2. querySelector 返回指定選擇器的第一個元素對象 切記 里面的選擇器需要加符號 .box #nav
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelectorAll()返回指定選擇器的所有元素對象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
</body>
1.3.4 獲取特殊元素(body,html)
1.4. 事件基礎
1.4.1. 事件概述
JavaScript 使我們有能力創建動態頁面,而事件是可以被 JavaScript 偵測到的行為。
簡單理解: 觸發--- 響應機制。
網頁中的每個元素都可以產生某些可以觸發 JavaScript 的事件,例如,我們可以在用戶點擊某按鈕時產生一個 事件,然后去執行某些操作。
1.4.2. 事件三要素
- 事件源(誰):觸發事件的元素
- 事件類型(什么事件): 例如 click 點擊事件
- 事件處理程序(做啥):事件觸發后要執行的代碼(函數形式),事件處理函數
案例代碼
<body>
<button id="btn">唐伯虎</button>
<script>
// 點擊一個按鈕,彈出對話框
// 1. 事件是有三部分組成 事件源 事件類型 事件處理程序 我們也稱為事件三要素
//(1) 事件源 事件被觸發的對象 誰 按鈕
var btn = document.getElementById('btn');
//(2) 事件類型 如何觸發 什么事件 比如鼠標點擊(onclick) 還是鼠標經過 還是鍵盤按下
//(3) 事件處理程序 通過一個函數賦值的方式 完成
btn.onclick = function() {
alert('點秋香');
}
</script>
</body>
1.4.3. 執行事件的步驟
案例代碼
<body>
<div>123</div>
<script>
// 執行事件步驟
// 點擊div 控制台輸出 我被選中了
// 1. 獲取事件源
var div = document.querySelector('div');
// 2.綁定事件 注冊事件
// div.onclick
// 3.添加事件處理程序
div.onclick = function() {
console.log('我被選中了');
}
</script>
</body>
1.4.4. 常見的鼠標事件
1.4.5. 分析事件三要素
-
下拉菜單三要素
-
關閉廣告三要素
1.5. 操作元素
JavaScript的 DOM 操作可以改變網頁內容、結構和樣式,我們可以利用 DOM 操作元素來改變元素里面的內容、屬性等。(注意:這些操作都是通過元素對象的屬性實現的)
1.5.1. 改變元素內容(獲取或設置)
innerText改變元素內容
<body>
<button>顯示當前系統時間</button>
<div>某個時間</div>
<p>1123</p>
<script>
// 當我們點擊了按鈕, div里面的文字會發生變化
// 1. 獲取元素
var btn = document.querySelector('button');
var div = document.querySelector('div');
// 2.注冊事件
btn.onclick = function() {
// div.innerText = '2019-6-6';
div.innerHTML = getDate();
}
function getDate() {
var date = new Date();
// 我們寫一個 2019年 5月 1日 星期三
var year = date.getFullYear();
var month = date.getMonth() + 1;
var dates = date.getDate();
var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
var day = date.getDay();
return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
}
</script>
</body>
innerText和innerHTML的區別
- 獲取內容時的區別:
innerText會去除空格和換行,而innerHTML會保留空格和換行
- 設置內容時的區別:
innerText不會識別html,而innerHTML會識別
案例代碼
<body>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML的區別
// 1. innerText 不識別html標簽 非標准 去除空格和換行
var div = document.querySelector('div');
// div.innerText = '<strong>今天是:</strong> 2019';
// 2. innerHTML 識別html標簽 W3C標准 保留空格和換行的
div.innerHTML = '<strong>今天是:</strong> 2019';
// 這兩個屬性是可讀寫的 可以獲取元素里面的內容
var p = document.querySelector('p');
console.log(p.innerText);
console.log(p.innerHTML);
</script>
</body>
1.5.2. 常用元素的屬性操作
獲取屬性的值
元素對象.屬性名
設置屬性的值
元素對象.屬性名 = 值
案例代碼
<body>
<button id="ldh">劉德華</button>
<button id="zxy">張學友</button> <br>
<img src="images/ldh.jpg" alt="" title="劉德華">
<script>
// 修改元素屬性 src
// 1. 獲取元素
var ldh = document.getElementById('ldh');
var zxy = document.getElementById('zxy');
var img = document.querySelector('img');
// 2. 注冊事件 處理程序
zxy.onclick = function() {
img.src = 'images/zxy.jpg';
img.title = '張學友思密達';
}
ldh.onclick = function() {
img.src = 'images/ldh.jpg';
img.title = '劉德華';
}
</script>
</body>
1.5.3. 案例:分時問候
1.5.4. 表單元素的屬性操作
獲取屬性的值
元素對象.屬性名
設置屬性的值
元素對象.屬性名 = 值
表單元素中有一些屬性如:disabled、checked、selected,元素對象的這些屬性的值是布爾型。
案例代碼
<body>
<button>按鈕</button>
<input type="text" value="輸入內容">
<script>
// 1. 獲取元素
var btn = document.querySelector('button');
var input = document.querySelector('input');
// 2. 注冊事件 處理程序
btn.onclick = function() {
// 表單里面的值 文字內容是通過 value 來修改的
input.value = '被點擊了';
// 如果想要某個表單被禁用 不能再點擊 disabled 我們想要這個按鈕 button禁用
// btn.disabled = true;
this.disabled = true;
// this 指向的是事件函數的調用者 btn
}
</script>
</body>
1.5.5. 案例:仿京東顯示密碼
1.5.6. 樣式屬性操作
我們可以通過 JS 修改元素的大小、顏色、位置等樣式。
常用方式
方式1:通過操作style屬性
元素對象的style屬性也是一個對象!
元素對象.style.樣式屬性 = 值;
案例代碼
<body>
<div></div>
<script>
// 1. 獲取元素
var div = document.querySelector('div');
// 2. 注冊事件 處理程序
div.onclick = function() {
// div.style里面的屬性 采取駝峰命名法
this.style.backgroundColor = 'purple';
this.style.width = '250px';
}
</script>
</body>
案例:淘寶點擊關閉二維碼
案例:循環精靈圖背景
案例:顯示隱藏文本框內容
方式2:通過操作className屬性
元素對象.className = 值;
因為class是關鍵字,所有使用className。
案例代碼
<body>
<div class="first">文本</div>
<script>
// 1. 使用 element.style 獲得修改元素樣式 如果樣式比較少 或者 功能簡單的情況下使用
var test = document.querySelector('div');
test.onclick = function() {
// this.style.backgroundColor = 'purple';
// this.style.color = '#fff';
// this.style.fontSize = '25px';
// this.style.marginTop = '100px';
// 2. 我們可以通過 修改元素的className更改元素的樣式 適合於樣式較多或者功能復雜的情況
// 3. 如果想要保留原先的類名,我們可以這么做 多類名選擇器
// this.className = 'change';
this.className = 'first change';
}
</script>
</body>
案例:密碼框格式提示錯誤信息
1.6. 今日總結
day02 - Web APIs
學習目標:
能夠說出排他操作的一般實現步驟
能夠使用html5中的dataset方式操作自定義屬性
能夠根據提示完成百度換膚的案例
能夠根據提示完成全選案例
能夠根據提示完成tab欄切換案例
能夠區分元素節點、文本節點、屬性節點
能夠獲取指定元素的父元素
能夠獲取指定元素的所有子元素
能夠說出childNodes和children的區別
能夠使用createElement創建頁面元素
1.1. 排他操作
1.1.1 排他思想
如果有同一組元素,我們想要某一個元素實現某種樣式, 需要用到循環的排他思想算法:
-
所有元素全部清除樣式(干掉其他人)
-
給當前元素設置樣式 (留下我自己)
-
注意順序不能顛倒,首先干掉其他人,再設置自己
<button>按鈕1</button>
<button>按鈕2</button>
<button>按鈕3</button>
<button>按鈕4</button>
<button>按鈕5</button>
<script>
// 1. 獲取所有按鈕元素
var btns = document.getElementsByTagName('button');
// btns得到的是偽數組 里面的每一個元素 btns[i]
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
// (1) 我們先把所有的按鈕背景顏色去掉 干掉所有人
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
// (2) 然后才讓當前的元素背景顏色為pink 留下我自己
this.style.backgroundColor = 'pink';
}
}
</script>
1.2 案例:百度換膚
<body>
<ul class="baidu">
<li><img src="images/1.jpg"></li>
<li><img src="images/2.jpg"></li>
<li><img src="images/3.jpg"></li>
<li><img src="images/4.jpg"></li>
</ul>
<script>
// 1. 獲取元素
var imgs = document.querySelector('.baidu').querySelectorAll('img');
// console.log(imgs);
// 2. 循環注冊事件
for (var i = 0; i < imgs.length; i++) {
imgs[i].onclick = function() {
// this.src 就是我們點擊圖片的路徑 images/2.jpg
// console.log(this.src);
// 把這個路徑 this.src 給body 就可以了
document.body.style.backgroundImage = 'url(' + this.src + ')';
}
}
</script>
</body>
1.3 案例:表格隔行變色
<script>
// 1.獲取元素 獲取的是 tbody 里面所有的行
var trs = document.querySelector('tbody').querySelectorAll('tr');
// 2. 利用循環綁定注冊事件
for (var i = 0; i < trs.length; i++) {
// 3. 鼠標經過事件 onmouseover
trs[i].onmouseover = function() {
// console.log(11);
this.className = 'bg';
}
// 4. 鼠標離開事件 onmouseout
trs[i].onmouseout = function() {
this.className = '';
}
}
</script>
1.4 案例:全選
<script>
// 1. 全選和取消全選做法: 讓下面所有復選框的checked屬性(選中狀態) 跟隨 全選按鈕即可
// 獲取元素
var j_cbAll = document.getElementById('j_cbAll');
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
// 全選按鈕注冊事件
j_cbAll.onclick = function() {
// this.checked 當前復選框的選中狀態
console.log(this.checked);
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
// 給所有的子復選框注冊單擊事件
for (var i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function() {
// flag 控制全選按鈕是否選中
var flag = true;
// 每次點擊下面的復選框都要循環檢查者4個小按鈕是否全被選中
for (var i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
flag = false;
break;
}
}
// 設置全選按鈕的狀態
j_cbAll.checked = flag;
}
}
</script>
1.5. 自定義屬性操作
1.5.1 獲取屬性值
<div id="demo" index="1" class="nav"></div>
<script>
var div = document.querySelector('div');
// 1. 獲取元素的屬性值
// (1) element.屬性
console.log(div.id);
//(2) element.getAttribute('屬性') get得到獲取 attribute 屬性的意思 我們程序員自己添加的屬性我們稱為自定義屬性 index
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
</script>
1.5.2. 設置屬性值
// 2. 設置元素屬性值
// (1) element.屬性= '值'
div.id = 'test';
div.className = 'navs';
// (2) element.setAttribute('屬性', '值'); 主要針對於自定義屬性
div.setAttribute('index', 2);
div.setAttribute('class', 'footer'); // class 特殊 這里面寫的就是
1.5.3. 移出屬性
// class 不是className
// 3 移除屬性 removeAttribute(屬性)
div.removeAttribute('index');
1.5.4. 案例:tab欄
<script>
// 獲取元素
var tab_list = document.querySelector('.tab_list');
var lis = tab_list.querySelectorAll('li');
var items = document.querySelectorAll('.item');
// for循環,給選項卡綁定點擊事件
for (var i = 0; i < lis.length; i++) {
// 開始給5個小li 設置索引號
lis[i].setAttribute('index', i);
lis[i].onclick = function() {
// 1. 上的模塊選項卡,當前這一個底色會是紅色,其余不變(排他思想)
// 干掉所有人 其余的li清除 class 這個類
for (var i = 0; i < lis.length; i++) {
lis[i].className = '';
}
// 留下我自己
this.className = 'current';
// 2. 下面的顯示內容模塊
var index = this.getAttribute('index');
console.log(index);
// 干掉所有人 讓其余的item 這些div 隱藏
for (var i = 0; i < items.length; i++) {
items[i].style.display = 'none';
}
// 留下我自己 讓對應的item 顯示出來
items[index].style.display = 'block';
}
}
</script>
1.5.5. H5自定義屬性
自定義屬性目的:是為了保存並使用數據。有些數據可以保存到頁面中而不用保存到數據庫中。
自定義屬性獲取是通過getAttribute(‘屬性’) 獲取。
但是有些自定義屬性很容易引起歧義,不容易判斷是元素的內置屬性還是自定義屬性。
H5給我們新增了自定義屬性:
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div = document.querySelector('div');
// console.log(div.getTime);
console.log(div.getAttribute('getTime'));
div.setAttribute('data-time', 20);
console.log(div.getAttribute('data-index'));
console.log(div.getAttribute('data-list-name'));
// h5新增的獲取自定義屬性的方法 它只能獲取data-開頭的
// dataset 是一個集合里面存放了所有以data開頭的自定義屬性
console.log(div.dataset);
console.log(div.dataset.index);
console.log(div.dataset['index']);
// 如果自定義屬性里面有多個-鏈接的單詞,我們獲取的時候采取 駝峰命名法
console.log(div.dataset.listName);
console.log(div.dataset['listName']);
</script>
1.6. 節點操作
1.6.1. 節點概述
網頁中的所有內容都是節點(標簽、屬性、文本、注釋等),在DOM 中,節點使用 node 來表示。
HTML DOM 樹中的所有節點均可通過 JavaScript 進行訪問,所有 HTML 元素(節點)均可被修改,也可以創建或刪除。
一般地,節點至少擁有nodeType(節點類型)、nodeName(節點名稱)和nodeValue(節點值)這三個基本屬性。
1.6.2. 節點層級
利用 DOM 樹可以把節點划分為不同的層級關系,常見的是父子兄層級關系。
1.6.3. 父級節點
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父節點 parentNode
var erweima = document.querySelector('.erweima');
// var box = document.querySelector('.box');
// 得到的是離元素最近的父級節點(親爸爸) 如果找不到父節點就返回為 null
console.log(erweima.parentNode);
</script>
1.6.4. 子節點
所有子節點
子元素節點
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// DOM 提供的方法(API)獲取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子節點 childNodes 所有的子節點 包含 元素節點 文本節點等等
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 2. children 獲取所有的子元素節點 也是我們實際開發常用的
console.log(ul.children);
</script>
第1個子節點
最后1個子節點
第1個子元素節點
最后1個子元素節點
實際開發中,firstChild 和 lastChild 包含其他節點,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性問題,那么我們如何獲取第一個子元素節點或最后一個子元素節點呢?
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一個子節點 不管是文本節點還是元素節點
console.log(ol.firstChild);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一個子元素節點 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 實際開發的寫法 既沒有兼容性問題又返回第一個子元素
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
1.6.5. 案例:新浪下拉菜單
<script>
// 1. 獲取元素
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到4個小li
// 2.循環注冊事件
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
1.6.6. 兄弟節點
下一個兄弟節點
上一個兄弟節點
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一個兄弟節點 包含元素節點或者 文本節點等等
console.log(div.nextSibling);
console.log(div.previousSibling);
// 2. nextElementSibling 得到下一個兄弟元素節點
console.log(div.nextElementSibling);
console.log(div.previousElementSibling);
</script>
下一個兄弟元素節點(有兼容性問題)
上一個兄弟元素節點(有兼容性問題)
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
1.6.7. 創建節點
1.6.8. 添加節點
<ul>
<li>123</li>
</ul>
<script>
// 1. 創建節點元素節點
var li = document.createElement('li');
// 2. 添加節點 node.appendChild(child) node 父級 child 是子級 后面追加元素
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3. 添加節點 node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 4. 我們想要頁面添加一個新的元素 : 1. 創建元素 2. 添加元素
</script>
1.6.9. 案例:簡單版發布留言
<body>
<textarea name="" id=""></textarea>
<button>發布</button>
<ul>
</ul>
<script>
// 1. 獲取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
// 2. 注冊事件
btn.onclick = function() {
if (text.value == '') {
alert('您沒有輸入內容');
return false;
} else {
// console.log(text.value);
// (1) 創建元素
var li = document.createElement('li');
// 先有li 才能賦值
li.innerHTML = text.value;
// (2) 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
}
}
</script>
</body>
day03 - Web APIs
學習目標:
能夠使用removeChild()方法刪除節點
能夠完成動態生成表格案例
能夠使用傳統方式和監聽方式給元素注冊事件
能夠說出事件流執行的三個階段
能夠在事件處理函數中獲取事件對象
能夠使用事件對象取消默認行為
能夠使用事件對象阻止事件冒泡
能夠使用事件對象獲取鼠標的位置
能夠完成跟隨鼠標的天使案例
1.1. 節點操作
1.1.1 刪除節點
node.removeChild() 方法從 node節點中刪除一個子節點,返回刪除的節點。
<button>刪除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光頭強</li>
</ul>
<script>
// 1.獲取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 刪除元素 node.removeChild(child)
// ul.removeChild(ul.children[0]);
// 3. 點擊按鈕依次刪除里面的孩子
btn.onclick = function() {
if (ul.children.length == 0) {
this.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
1.1.2 案例:刪除留言
<textarea name="" id=""></textarea>
<button>發布</button>
<ul>
</ul>
<script>
// 1. 獲取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
// 2. 注冊事件
btn.onclick = function() {
if (text.value == '') {
alert('您沒有輸入內容');
return false;
} else {
// console.log(text.value);
// (1) 創建元素
var li = document.createElement('li');
// 先有li 才能賦值
li.innerHTML = text.value + "<a href='javascript:;'>刪除</a>";
// (2) 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
// (3) 刪除元素 刪除的是當前鏈接的li 它的父親
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
// 刪除的是 li 當前a所在的li this.parentNode;
ul.removeChild(this.parentNode);
}
}
}
}
</script>
1.1.3 復制(克隆)節點
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 括號為空或者里面是false 淺拷貝 只復制標簽不復制里面的內容
// 2. node.cloneNode(true); 括號為true 深拷貝 復制標簽復制里面的內容
var lili = ul.children[0].cloneNode(true);
ul.appendChild(lili);
</script>
1.1.4 案例:動態生成表格
<script>
// 1.先去准備好學生的數據
var datas = [{
name: '魏瓔珞',
subject: 'JavaScript',
score: 100
}, {
name: '弘歷',
subject: 'JavaScript',
score: 98
}, {
name: '傅恆',
subject: 'JavaScript',
score: 99
}, {
name: '明玉',
subject: 'JavaScript',
score: 88
}, {
name: '大豬蹄子',
subject: 'JavaScript',
score: 0
}];
// 2. 往tbody 里面創建行: 有幾個人(通過數組的長度)我們就創建幾行
var tbody = document.querySelector('tbody');
// 遍歷數組
for (var i = 0; i < datas.length; i++) {
// 1. 創建 tr行
var tr = document.createElement('tr');
tbody.appendChild(tr);
// 2. 行里面創建單元格td 單元格的數量取決於每個對象里面的屬性個數
// 使用for in遍歷學生對象
for (var k in datas[i]) {
// 創建單元格
var td = document.createElement('td');
// 把對象里面的屬性值 datas[i][k] 給 td
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
// 3. 創建有刪除2個字的單元格
var td = document.createElement('td');
td.innerHTML = '<a href="javascript:;">刪除 </a>';
tr.appendChild(td);
}
// 4. 刪除操作 開始
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
// 點擊a 刪除 當前a 所在的行(鏈接的爸爸的爸爸) node.removeChild(child)
tbody.removeChild(this.parentNode.parentNode)
}
}
</script>
1.1.5 創建元素的三種方式
<script>
// 三種創建元素方式區別
// 1. document.write() 創建元素 如果頁面文檔流加載完畢,再調用這句話會導致頁面重繪
var btn = document.querySelector('button');
btn.onclick = function() {
document.write('<div>123</div>');
}
// 2. innerHTML 創建元素
var inner = document.querySelector('.inner');
for (var i = 0; i <= 100; i++) {
inner.innerHTML += '<a href="#">百度</a>'
}
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3. document.createElement() 創建元素
var create = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
1.1.6 innerTHML和createElement效率對比
innerHTML字符串拼接方式(效率低)
<script>
function fn() {
var d1 = +new Date();
var str = '';
for (var i = 0; i < 1000; i++) {
document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
createElement方式(效率一般)
<script>
function fn() {
var d1 = +new Date();
for (var i = 0; i < 1000; i++) {
var div = document.createElement('div');
div.style.width = '100px';
div.style.height = '2px';
div.style.border = '1px solid red';
document.body.appendChild(div);
}
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
innerHTML數組方式(效率高)
<script>
function fn() {
var d1 = +new Date();
var array = [];
for (var i = 0; i < 1000; i++) {
array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
}
document.body.innerHTML = array.join('');
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>
1.2. DOM的核心總結
關於dom操作,我們主要針對於元素的操作。主要有創建、增、刪、改、查、屬性操作、事件操作。
1.2.1. 創建
1.2.2. 增加
1.2.3. 刪
1.2.4. 改
1.2.5. 查
1.2.6. 屬性操作
1.2.7. 事件操作(重點)
1.3. 事件高級
1.3.1. 注冊事件(2種方式)
1.3.2 事件監聽
addEventListener()事件監聽(IE9以后支持)
eventTarget.addEventListener()方法將指定的監聽器注冊到 eventTarget(目標對象)上,當該對象觸發指定的事件時,就會執行事件處理函數。
attacheEvent()事件監聽(IE678支持)
eventTarget.attachEvent()方法將指定的監聽器注冊到 eventTarget(目標對象) 上,當該對象觸發指定的事件時,指定的回調函數就會被執行。
<button>傳統注冊事件</button>
<button>方法監聽注冊事件</button>
<button>ie9 attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// 1. 傳統方式注冊事件
btns[0].onclick = function() {
alert('hi');
}
btns[0].onclick = function() {
alert('hao a u');
}
// 2. 事件偵聽注冊事件 addEventListener
// (1) 里面的事件類型是字符串 必定加引號 而且不帶on
// (2) 同一個元素 同一個事件可以添加多個偵聽器(事件處理程序)
btns[1].addEventListener('click', function() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
事件監聽兼容性解決方案
封裝一個函數,函數中判斷瀏覽器的類型:
1.3.3. 刪除事件(解綁事件)
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySelectorAll('div');
divs[0].onclick = function() {
alert(11);
// 1. 傳統方式刪除事件
divs[0].onclick = null;
}
// 2. removeEventListener 刪除事件
divs[1].addEventListener('click', fn) // 里面的fn 不需要調用加小括號
function fn() {
alert(22);
divs[1].removeEventListener('click', fn);
}
// 3. detachEvent
divs[2].attachEvent('onclick', fn1);
function fn1() {
alert(33);
divs[2].detachEvent('onclick', fn1);
}
</script>
**刪除事件兼容性解決方案 **
1.3.4. DOM事件流
html中的標簽都是相互嵌套的,我們可以將元素想象成一個盒子裝一個盒子,document是最外面的大盒子。 當你單擊一個div時,同時你也單擊了div的父元素,甚至整個頁面。 那么是先執行父元素的單擊事件,還是先執行div的單擊事件 ???
比如:我們給頁面中的一個div注冊了單擊事件,當你單擊了div時,也就單擊了body,單擊了html,單擊了document。
當時的2大瀏覽器霸主誰也不服誰! IE 提出從目標元素開始,然后一層一層向外接收事件並響應,也就是冒泡型事件流。 Netscape(網景公司)提出從最外層開始,然后一層一層向內接收事件並響應,也就是捕獲型事件流。 江湖紛爭,武林盟主也腦殼疼!!! 最終,w3c 采用折中的方式,平息了戰火,制定了統一的標准 —--— 先捕獲再冒泡。 現代瀏覽器都遵循了此標准,所以當事件發生時,會經歷3個階段。
DOM 事件流會經歷3個階段:
-
捕獲階段
-
當前目標階段
-
冒泡階段
我們向水里面扔一塊石頭,首先它會有一個下降的過程,這個過程就可以理解為從最頂層向事件發生的最具體元素(目標點)的捕獲過程;之后會產生泡泡,會在最低點( 最具體元素)之后漂浮到水面上,這個過程相當於事件冒泡。
事件冒泡
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// onclick 和 attachEvent(ie) 在冒泡階段觸發
// 冒泡階段 如果addEventListener 第三個參數是 false 或者 省略
// son -> father ->body -> html -> document
var son = document.querySelector('.son');
// 給son注冊單擊事件
son.addEventListener('click', function() {
alert('son');
}, false);
// 給father注冊單擊事件
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
// 給document注冊單擊事件,省略第3個參數
document.addEventListener('click', function() {
alert('document');
})
</script>
事件捕獲
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 如果addEventListener() 第三個參數是 true 那么在捕獲階段觸發
// document -> html -> body -> father -> son
var son = document.querySelector('.son');
// 給son注冊單擊事件,第3個參數為true
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
// 給father注冊單擊事件,第3個參數為true
father.addEventListener('click', function() {
alert('father');
}, true);
// 給document注冊單擊事件,第3個參數為true
document.addEventListener('click', function() {
alert('document');
}, true)
</script>
1.3.5. 事件對象
什么是事件對象
事件發生后,跟事件相關的一系列信息數據的集合都放到這個對象里面,這個對象就是事件對象。
比如:
-
誰綁定了這個事件。
-
鼠標觸發事件的話,會得到鼠標的相關信息,如鼠標位置。
-
鍵盤觸發事件的話,會得到鍵盤的相關信息,如按了哪個鍵。
事件對象的使用
事件觸發發生時就會產生事件對象,並且系統會以實參的形式傳給事件處理函數。
所以,在事件處理函數中聲明1個形參用來接收事件對象。
事件對象的兼容性處理
事件對象本身的獲取存在兼容問題:
-
標准瀏覽器中是瀏覽器給方法傳遞的參數,只需要定義形參 e 就可以獲取到。
-
在 IE6~8 中,瀏覽器不會給方法傳遞參數,如果需要的話,需要到 window.event 中獲取查找。
只要“||”前面為false, 不管“||”后面是true 還是 false,都返回 “||” 后面的值。
只要“||”前面為true, 不管“||”后面是true 還是 false,都返回 “||” 前面的值。
<div>123</div>
<script>
var div = document.querySelector('div');
div.onclick = function(e) {
// 事件對象
e = e || window.event;
console.log(e);
}
</script>
事件對象的屬性和方法
e.target 和 this 的區別
-
this 是事件綁定的元素(綁定這個事件處理函數的元素) 。
-
e.target 是事件觸發的元素。
常情況下terget 和 this是一致的, 但有一種情況不同,那就是在事件冒泡時(父子元素有相同事件,單擊子元素,父元素的事件處理函數也會被觸發執行), 這時候this指向的是父元素,因為它是綁定事件的元素對象, 而target指向的是子元素,因為他是觸發事件的那個具體元素對象。
<div>123</div>
<script>
var div = document.querySelector('div');
div.addEventListener('click', function(e) {
// e.target 和 this指向的都是div
console.log(e.target);
console.log(this);
});
</script>
事件冒泡下的e.target和this
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我們給ul 綁定了事件 那么this 就指向ul
console.log(this); // ul
// e.target 觸發了事件的對象 我們點擊的是li e.target 指向的就是li
console.log(e.target); // li
});
</script>
1.3.6 阻止默認行為
html中一些標簽有默認行為,例如a標簽被單擊后,默認會進行頁面跳轉。
<a href="http://www.baidu.com">百度</a>
<script>
// 2. 阻止默認行為 讓鏈接不跳轉
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 標准寫法
});
// 3. 傳統的注冊方式
a.onclick = function(e) {
// 普通瀏覽器 e.preventDefault(); 方法
e.preventDefault();
// 低版本瀏覽器 ie678 returnValue 屬性
e.returnValue = false;
// 我們可以利用return false 也能阻止默認行為 沒有兼容性問題
return false;
}
</script>
1.3.7 阻止事件冒泡
事件冒泡本身的特性,會帶來的壞處,也會帶來的好處。
<div class="father">
<div class="son">son兒子</div>
</div>
<script>
var son = document.querySelector('.son');
// 給son注冊單擊事件
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 傳播
window.event.cancelBubble = true; // 非標准 cancel 取消 bubble 泡泡
}, false);
var father = document.querySelector('.father');
// 給father注冊單擊事件
father.addEventListener('click', function() {
alert('father');
}, false);
// 給document注冊單擊事件
document.addEventListener('click', function() {
alert('document');
})
</script>
阻止事件冒泡的兼容性處理
1.3.8 事件委托
事件冒泡本身的特性,會帶來的壞處,也會帶來的好處。
什么是事件委托
把事情委托給別人,代為處理。
事件委托也稱為事件代理,在 jQuery 里面稱為事件委派。
說白了就是,不給子元素注冊事件,給父元素注冊事件,把處理代碼在父元素的事件中執行。
生活中的代理:
js事件中的代理:
事件委托的原理
給父元素注冊事件,利用事件冒泡,當子元素的事件觸發,會冒泡到父元素,然后去控制相應的子元素。
事件委托的作用
-
我們只操作了一次 DOM ,提高了程序的性能。
-
動態新創建的子元素,也擁有事件。
<ul>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
</ul>
<script>
// 事件委托的核心原理:給父節點添加偵聽器, 利用事件冒泡影響每一個子節點
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// e.target 這個可以得到我們點擊的對象
e.target.style.backgroundColor = 'pink';
})
</script>
1.4. 常用鼠標事件
1.4.1 案例:禁止選中文字和禁止右鍵菜單
<body>
我是一段不願意分享的文字
<script>
// 1. contextmenu 我們可以禁用右鍵菜單
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
})
// 2. 禁止選中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
1.4.2 鼠標事件對象
1.4.3 獲取鼠標在頁面的坐標
<script>
// 鼠標事件對象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠標在可視區的x和y坐標
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠標在頁面文檔的x和y坐標
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠標在電腦屏幕的x和y坐標
console.log(e.screenX);
console.log(e.screenY);
})
</script>
1.4.4 案例:跟隨鼠標的天使
<img src="images/angel.gif" alt="">
<script>
var pic = document.querySelector('img');
document.addEventListener('mousemove', function(e) {
// 1. mousemove只要我們鼠標移動1px 就會觸發這個事件
// 2.核心原理: 每次鼠標移動,我們都會獲得最新的鼠標坐標,
// 把這個x和y坐標做為圖片的top和left 值就可以移動圖片
var x = e.pageX;
var y = e.pageY;
console.log('x坐標是' + x, 'y坐標是' + y);
//3 . 千萬不要忘記給left 和top 添加px 單位
pic.style.left = x - 50 + 'px';
pic.style.top = y - 40 + 'px';
});
</script>
day04 - Web APIs
學習目標:
能夠說出常用的3-5個鍵盤事件
能夠知道如何獲取當前鍵盤按下的是哪個鍵
能夠知道瀏覽器的頂級對象window
能夠使用window.onload事件
能夠使用window.onresize事件
能夠說出兩種定時器的區別
能夠使用location對象的href屬性完成頁面之間的跳轉
能夠使用location對象獲取url中的參數部分
能夠使用history提供的方法實現頁面刷新
1.1. 常用的鍵盤事件
1.1.1 鍵盤事件
<script>
// 常用的鍵盤事件
//1. keyup 按鍵彈起的時候觸發
document.addEventListener('keyup', function() {
console.log('我彈起了');
})
//3. keypress 按鍵按下的時候觸發 不能識別功能鍵 比如 ctrl shift 左右箭頭啊
document.addEventListener('keypress', function() {
console.log('我按下了press');
})
//2. keydown 按鍵按下的時候觸發 能識別功能鍵 比如 ctrl shift 左右箭頭啊
document.addEventListener('keydown', function() {
console.log('我按下了down');
})
// 4. 三個事件的執行順序 keydown -- keypress -- keyup
</script>
1.1.2 鍵盤事件對象
使用keyCode屬性判斷用戶按下哪個鍵
<script>
// 鍵盤事件對象中的keyCode屬性可以得到相應鍵的ASCII碼值
document.addEventListener('keyup', function(e) {
console.log('up:' + e.keyCode);
// 我們可以利用keycode返回的ASCII碼值來判斷用戶按下了那個鍵
if (e.keyCode === 65) {
alert('您按下的a鍵');
} else {
alert('您沒有按下a鍵')
}
})
document.addEventListener('keypress', function(e) {
// console.log(e);
console.log('press:' + e.keyCode);
})
</script>
1.1.3 案例:模擬京東按鍵輸入內容
當我們按下 s 鍵, 光標就定位到搜索框(文本框獲得焦點)。
注意:觸發獲得焦點事件,可以使用 元素對象.focus()
<input type="text">
<script>
// 獲取輸入框
var search = document.querySelector('input');
// 給document注冊keyup事件
document.addEventListener('keyup', function(e) {
// 判斷keyCode的值
if (e.keyCode === 83) {
// 觸發輸入框的獲得焦點事件
search.focus();
}
})
</script>
1.1.4 案例:模擬京東快遞單號查詢
要求:當我們在文本框中輸入內容時,文本框上面自動顯示大字號的內容。
<div class="search">
<div class="con">123</div>
<input type="text" placeholder="請輸入您的快遞單號" class="jd">
</div>
<script>
// 獲取要操作的元素
var con = document.querySelector('.con');
var jd_input = document.querySelector('.jd');
// 給輸入框注冊keyup事件
jd_input.addEventListener('keyup', function() {
// 判斷輸入框內容是否為空
if (this.value == '') {
// 為空,隱藏放大提示盒子
con.style.display = 'none';
} else {
// 不為空,顯示放大提示盒子,設置盒子的內容
con.style.display = 'block';
con.innerText = this.value;
}
})
// 給輸入框注冊失去焦點事件,隱藏放大提示盒子
jd_input.addEventListener('blur', function() {
con.style.display = 'none';
})
// 給輸入框注冊獲得焦點事件
jd_input.addEventListener('focus', function() {
// 判斷輸入框內容是否為空
if (this.value !== '') {
// 不為空則顯示提示盒子
con.style.display = 'block';
}
})
</script>
1.2. BOM
1.2.1. 什么是BOM
BOM(Browser Object Model)即瀏覽器對象模型,它提供了獨立於內容而與瀏覽器窗口進行交互的對象,其核心對象是 window。
BOM 由一系列相關的對象構成,並且每個對象都提供了很多方法與屬性。
BOM 缺乏標准,JavaScript 語法的標准化組織是 ECMA,DOM 的標准化組織是 W3C,BOM 最初是Netscape 瀏覽器標准的一部分。
1.2.2. BOM的構成
BOM 比 DOM 更大,它包含 DOM。
1.2.3. 頂級對象window
1.2.4. window對象的常見事件
頁面(窗口)加載事件(2種)
第1種
window.onload 是窗口 (頁面)加載事件,當文檔內容完全加載完成會觸發該事件(包括圖像、腳本文件、CSS 文件等), 就調用的處理函數。
第2種
DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片,flash等等。
IE9以上才支持!!!
如果頁面的圖片很多的話, 從用戶訪問到onload觸發可能需要較長的時間, 交互效果就不能實現,必然影響用戶的體驗,此時用 DOMContentLoaded 事件比較合適。
<script>
window.addEventListener('load', function() {
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
alert('點擊我');
})
})
window.addEventListener('load', function() {
alert(22);
})
document.addEventListener('DOMContentLoaded', function() {
alert(33);
})
</script>
調整窗口大小事件
window.onresize 是調整窗口大小加載事件, 當觸發時就調用的處理函數。
注意:
-
只要窗口大小發生像素變化,就會觸發這個事件。
-
我們經常利用這個事件完成響應式布局。 window.innerWidth 當前屏幕的寬度
<script>
// 注冊頁面加載事件
window.addEventListener('load', function() {
var div = document.querySelector('div');
// 注冊調整窗口大小事件
window.addEventListener('resize', function() {
// window.innerWidth 獲取窗口大小
console.log('變化了');
if (window.innerWidth <= 800) {
div.style.display = 'none';
} else {
div.style.display = 'block';
}
})
})
</script>
<div></div>
1.2.5. 定時器(兩種)
window 對象給我們提供了 2 個非常好用的方法-定時器。
-
setTimeout()
-
setInterval()
setTimeout() 炸彈定時器
開啟定時器
普通函數是按照代碼順序直接調用。 簡單理解: 回調,就是回頭調用的意思。上一件事干完,再回頭再調用這個函數。 例如:定時器中的調用函數,事件處理函數,也是回調函數。 以前我們講的 element.onclick = function(){} 或者 element.addEventListener(“click”, fn); 里面的 函數也是回調函數。
<script>
// 回調函數是一個匿名函數
setTimeout(function() {
console.log('時間到了');
}, 2000);
function callback() {
console.log('爆炸了');
}
// 回調函數是一個有名函數
var timer1 = setTimeout(callback, 3000);
var timer2 = setTimeout(callback, 5000);
</script>
案例:5秒后關閉廣告
<body>
<img src="images/ad.jpg" alt="" class="ad">
<script>
// 獲取要操作的元素
var ad = document.querySelector('.ad');
// 開啟定時器
setTimeout(function() {
ad.style.display = 'none';
}, 5000);
</script>
</body>
停止定時器
<button>點擊停止定時器</button>
<script>
var btn = document.querySelector('button');
// 開啟定時器
var timer = setTimeout(function() {
console.log('爆炸了');
}, 5000);
// 給按鈕注冊單擊事件
btn.addEventListener('click', function() {
// 停止定時器
clearTimeout(timer);
})
</script>
setInterval() 鬧鍾定時器
開啟定時器
<script>
// 1. setInterval
setInterval(function() {
console.log('繼續輸出');
}, 1000);
</script>
案例:倒計時
<div>
<span class="hour">1</span>
<span class="minute">2</span>
<span class="second">3</span>
</div>
<script>
// 1. 獲取元素(時分秒盒子)
var hour = document.querySelector('.hour'); // 小時的黑色盒子
var minute = document.querySelector('.minute'); // 分鍾的黑色盒子
var second = document.querySelector('.second'); // 秒數的黑色盒子
var inputTime = +new Date('2019-5-1 18:00:00'); // 返回的是用戶輸入時間總的毫秒數
countDown(); // 我們先調用一次這個函數,防止第一次刷新頁面有空白
// 2. 開啟定時器
setInterval(countDown, 1000);
function countDown() {
var nowTime = +new Date(); // 返回的是當前時間總的毫秒數
var times = (inputTime - nowTime) / 1000; // times是剩余時間總的秒數
var h = parseInt(times / 60 / 60 % 24); //時
h = h < 10 ? '0' + h : h;
hour.innerHTML = h; // 把剩余的小時給 小時黑色盒子
var m = parseInt(times / 60 % 60); // 分
m = m < 10 ? '0' + m : m;
minute.innerHTML = m;
var s = parseInt(times % 60); // 當前的秒
s = s < 10 ? '0' + s : s;
second.innerHTML = s;
}
</script>
停止定時器
案例:發送短信倒計時
點擊按鈕后,該按鈕60秒之內不能再次點擊,防止重復發送短信。
手機號碼: <input type="number"> <button>發送</button>
<script>
var btn = document.querySelector('button');
// 全局變量,定義剩下的秒數
var time = 3;
// 注冊單擊事件
btn.addEventListener('click', function() {
// 禁用按鈕
btn.disabled = true;
// 開啟定時器
var timer = setInterval(function() {
// 判斷剩余秒數
if (time == 0) {
// 清除定時器和復原按鈕
clearInterval(timer);
btn.disabled = false;
btn.innerHTML = '發送';
} else {
btn.innerHTML = '還剩下' + time + '秒';
time--;
}
}, 1000);
});
</script>
1.2.6. this指向問題
this的指向在函數定義的時候是確定不了的,只有函數執行的時候才能確定this到底指向誰,一般情況下this的最終指向的是那個調用它的對象。
現階段,我們先了解一下幾個this指向
-
全局作用域或者普通函數中this指向全局對象window(注意定時器里面的this指向window)
-
方法調用中誰調用this指向誰
-
構造函數中this指向構造函數的實例
<button>點擊</button>
<script>
// this 指向問題 一般情況下this的最終指向的是那個調用它的對象
// 1. 全局作用域或者普通函數中this指向全局對象window( 注意定時器里面的this指向window)
console.log(this);
function fn() {
console.log(this);
}
window.fn();
window.setTimeout(function() {
console.log(this);
}, 1000);
// 2. 方法調用中誰調用this指向誰
var o = {
sayHi: function() {
console.log(this); // this指向的是 o 這個對象
}
}
o.sayHi();
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
console.log(this); // 事件處理函數中的this指向的是btn這個按鈕對象
})
// 3. 構造函數中this指向構造函數的實例
function Fun() {
console.log(this); // this 指向的是fun 實例對象
}
var fun = new Fun();
</script>
1.2.7. location對象
什么是 location 對象
URL
location 對象的屬性
案例:5分鍾自動跳轉頁面
<button>點擊</button>
<div></div>
<script>
var btn = document.querySelector('button');
var div = document.querySelector('div');
btn.addEventListener('click', function() {
// console.log(location.href);
location.href = 'http://www.itcast.cn';
})
var timer = 5;
setInterval(function() {
if (timer == 0) {
location.href = 'http://www.itcast.cn';
} else {
div.innerHTML = '您將在' + timer + '秒鍾之后跳轉到首頁';
timer--;
}
}, 1000);
</script>
案例:獲取URL參數
<div></div>
<script>
console.log(location.search); // ?uname=andy
// 1.先去掉? substr('起始的位置',截取幾個字符);
var params = location.search.substr(1); // uname=andy
console.log(params);
// 2. 利用=把字符串分割為數組 split('=');
var arr = params.split('=');
console.log(arr); // ["uname", "ANDY"]
var div = document.querySelector('div');
// 3.把數據寫入div中
div.innerHTML = arr[1] + '歡迎您';
</script>
location對象的常見方法
<button>點擊</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
// 記錄瀏覽歷史,所以可以實現后退功能
// location.assign('http://www.itcast.cn');
// 不記錄瀏覽歷史,所以不可以實現后退功能
// location.replace('http://www.itcast.cn');
location.reload(true);
})
</script>
1.2.8. navigator對象
navigator 對象包含有關瀏覽器的信息,它有很多屬性,我們最常用的是 userAgent,該屬性可以返回由客戶機發送服務器的 user-agent 頭部的值。
下面前端代碼可以判斷用戶那個終端打開頁面,實現跳轉
if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
window.location.href = ""; //手機
} else {
window.location.href = ""; //電腦
}
1.2.9 history對象
window對象給我們提供了一個 history對象,與瀏覽器歷史記錄進行交互。該對象包含用戶(在瀏覽器窗口中)訪問過的URL。
history對象一般在實際開發中比較少用,但是會在一些 OA 辦公系統中見到。
1.3. JS執行機制
以下代碼執行的結果是什么?
console.log(1);
setTimeout(function () {
console.log(3);
}, 1000);
console.log(2);
以下代碼執行的結果是什么?
console.log(1);
setTimeout(function () {
console.log(3);
}, 0);
console.log(2);
1.3.1 JS 是單線程
單線程就意味着,所有任務需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等着。
這樣所導致的問題是: 如果 JS 執行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染加載阻塞的感覺。
1.3.2 同步任務和異步任務
單線程導致的問題就是后面的任務等待前面任務完成,如果前面任務很耗時(比如讀取網絡數據),后面任務不得不一直等待!!
為了解決這個問題,利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標准,允許 JavaScript 腳本創建多個線程,但是子線程完全受主線程控制。於是,JS 中出現了同步任務和異步任務。
同步
前一個任務結束后再執行后一個任務,程序的執行順序與任務的排列順序是一致的、同步的。比如做飯的同步做法:我們要燒水煮飯,等水開了(10分鍾之后),再去切菜,炒菜。
異步
你在做一件事情時,因為這件事情會花費很長時間,在做這件事的同時,你還可以去處理其他事情。比如做飯的異步做法,我們在燒水的同時,利用這10分鍾,去切菜,炒菜。
JS中所有任務可以分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous)。 同步任務指的是: 在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務; 異步任務指的是: 不進入主線程、而進入”任務隊列”的任務,當主線程中的任務運行完了,才會從”任務隊列”取出異步任務放入主線程執行。
1.3.3 JS執行機制(事件循環)
1.3.4 代碼思考題
console.log(1);
document.onclick = function() {
console.log('click');
}
setTimeout(function() {
console.log(3)
}, 3000)
console.log(2);
day05 - Web APIs
學習目標:
能夠說出常見 offset 系列屬性的作用
能夠說出常見 client 系列屬性的作用
能夠說出常見 scroll 系列屬性的作用
能夠封裝簡單動畫函數
1.1. 元素偏移量 offset 系列
1.1.1 offset 概述
offset 翻譯過來就是偏移量, 我們使用 offset系列相關屬性可以動態的得到該元素的位置(偏移)、大小等。
-
獲得元素距離帶有定位父元素的位置
-
獲得元素自身的大小(寬度高度)
-
注意:返回的數值都不帶單位
1.1.2 offset 與 style 區別
offset
-
offset 可以得到任意樣式表中的樣式值
-
offset 系列獲得的數值是沒有單位的
-
offsetWidth 包含padding+border+width
-
offsetWidth 等屬性是只讀屬性,只能獲取不能賦值
-
所以,我們想要獲取元素大小位置,用offset更合適
style
-
style 只能得到行內樣式表中的樣式值
-
style.width 獲得的是帶有單位的字符串
-
style.width 獲得不包含padding和border 的值
-
style.width 是可讀寫屬性,可以獲取也可以賦值
-
所以,我們想要給元素更改值,則需要用style改變
因為平時我們都是給元素注冊觸摸事件,所以重點記住 targetTocuhes
1.1.3 案例:獲取鼠標在盒子內的坐標
- 我們在盒子內點擊,想要得到鼠標距離盒子左右的距離。
- 首先得到鼠標在頁面中的坐標(e.pageX, e.pageY)
- 其次得到盒子在頁面中的距離 ( box.offsetLeft, box.offsetTop)
- 用鼠標距離頁面的坐標減去盒子在頁面中的距離,得到 鼠標在盒子內的坐標
- 如果想要移動一下鼠標,就要獲取最新的坐標,使用鼠標移動
var box = document.querySelector('.box');
box.addEventListener('mousemove', function(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
this.innerHTML = 'x坐標是' + x + ' y坐標是' + y;
})
1.1.4 案例:模態框拖拽
彈出框,我們也稱為模態框。
1.點擊彈出層,會彈出模態框, 並且顯示灰色半透明的遮擋層。
2.點擊關閉按鈕,可以關閉模態框,並且同時關閉灰色半透明遮擋層。
3.鼠標放到模態框最上面一行,可以按住鼠標拖拽模態框在頁面中移動。
4.鼠標松開,可以停止拖動模態框移動
1.1.5. 案例分析:
- 點擊彈出層, 模態框和遮擋層就會顯示出來 display:block;
- 點擊關閉按鈕,模態框和遮擋層就會隱藏起來 display:none;
- 在頁面中拖拽的原理:鼠標按下並且移動, 之后松開鼠標
- 觸發事件是鼠標按下mousedown,鼠標移動mousemove 鼠標松開 mouseup
- 拖拽過程: 鼠標移動過程中,獲得最新的值賦值給模態框的left和top值,這樣模態框可以跟着鼠標走了
- 鼠標按下觸發的事件源是最上面一行,就是 id 為 title
- 鼠標的坐標減去 鼠標在盒子內的坐標, 才是模態框真正的位置。
- 鼠標按下,我們要得到鼠標在盒子的坐標。
- 鼠標移動,就讓模態框的坐標 設置為 :鼠標坐標 減去盒子坐標即可,注意移動事件寫到按下事件里面。
- 鼠標松開,就停止拖拽,就是可以讓鼠標移動事件解除
// 1. 獲取元素
var login = document.querySelector('.login');
var mask = document.querySelector('.login-bg');
var link = document.querySelector('#link');
var closeBtn = document.querySelector('#closeBtn');
var title = document.querySelector('#title');
// 2. 點擊彈出層這個鏈接 link 讓mask 和login 顯示出來
link.addEventListener('click', function() {
mask.style.display = 'block';
login.style.display = 'block';
})
// 3. 點擊 closeBtn 就隱藏 mask 和 login
closeBtn.addEventListener('click', function() {
mask.style.display = 'none';
login.style.display = 'none';
})
// 4. 開始拖拽
// (1) 當我們鼠標按下, 就獲得鼠標在盒子內的坐標
title.addEventListener('mousedown', function(e) {
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
// (2) 鼠標移動的時候,把鼠標在頁面中的坐標,減去 鼠標在盒子內的坐標就是模態框的left和top值
document.addEventListener('mousemove', move)
function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
// (3) 鼠標彈起,就讓鼠標移動事件移除
document.addEventListener('mouseup', function() {
document.removeEventListener('mousemove', move);
})
})
1.1.6 案例:仿京東放大鏡
- 整個案例可以分為三個功能模塊
- 鼠標經過小圖片盒子, 黃色的遮擋層 和 大圖片盒子顯示,離開隱藏2個盒子功能
- 黃色的遮擋層跟隨鼠標功能。
- 移動黃色遮擋層,大圖片跟隨移動功能。
1.1.7. 案例分析:
- 黃色的遮擋層跟隨鼠標功能。
- 把鼠標坐標給遮擋層不合適。因為遮擋層坐標以父盒子為准。
- 首先是獲得鼠標在盒子的坐標。
- 之后把數值給遮擋層做為left 和top值。
- 此時用到鼠標移動事件,但是還是在小圖片盒子內移動。
- 發現,遮擋層位置不對,需要再減去盒子自身高度和寬度的一半。
- 遮擋層不能超出小圖片盒子范圍。
- 如果小於零,就把坐標設置為0
- 如果大於遮擋層最大的移動距離,就把坐標設置為最大的移動距離
- 遮擋層的最大移動距離:小圖片盒子寬度 減去 遮擋層盒子寬度
window.addEventListener('load', function() {
var preview_img = document.querySelector('.preview_img');
var mask = document.querySelector('.mask');
var big = document.querySelector('.big');
// 1. 當我們鼠標經過 preview_img 就顯示和隱藏 mask 遮擋層 和 big 大盒子
preview_img.addEventListener('mouseover', function() {
mask.style.display = 'block';
big.style.display = 'block';
})
preview_img.addEventListener('mouseout', function() {
mask.style.display = 'none';
big.style.display = 'none';
})
// 2. 鼠標移動的時候,讓黃色的盒子跟着鼠標來走
preview_img.addEventListener('mousemove', function(e) {
// (1). 先計算出鼠標在盒子內的坐標
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
// console.log(x, y);
// (2) 減去盒子高度 300的一半 是 150 就是我們mask 的最終 left 和top值了
// (3) 我們mask 移動的距離
var maskX = x - mask.offsetWidth / 2;
var maskY = y - mask.offsetHeight / 2;
// (4) 如果x 坐標小於了0 就讓他停在0 的位置
// 遮擋層的最大移動距離
var maskMax = preview_img.offsetWidth - mask.offsetWidth;
if (maskX <= 0) {
maskX = 0;
} else if (maskX >= maskMax) {
maskX = maskMax;
}
if (maskY <= 0) {
maskY = 0;
} else if (maskY >= maskMax) {
maskY = maskMax;
}
mask.style.left = maskX + 'px';
mask.style.top = maskY + 'px';
// 3. 大圖片的移動距離 = 遮擋層移動距離 * 大圖片最大移動距離 / 遮擋層的最大移動距離
// 大圖
var bigIMg = document.querySelector('.bigImg');
// 大圖片最大移動距離
var bigMax = bigIMg.offsetWidth - big.offsetWidth;
// 大圖片的移動距離 X Y
var bigX = maskX * bigMax / maskMax;
var bigY = maskY * bigMax / maskMax;
bigIMg.style.left = -bigX + 'px';
bigIMg.style.top = -bigY + 'px';
})
})
1.2. 元素可視區 client 系列
1.2.1 client概述
client 翻譯過來就是客戶端,我們使用 client 系列的相關屬性來獲取元素可視區的相關信息。通過 client
系列的相關屬性可以動態的得到該元素的邊框大小、元素大小等。
1.2.2. 淘寶 flexible.js 源碼分析
立即執行函數 (function(){})() 或者 (function(){}())
主要作用: 創建一個獨立的作用域。 避免了命名沖突問題
下面三種情況都會刷新頁面都會觸發 load 事件。
1.a標簽的超鏈接
2.F5或者刷新按鈕(強制刷新)
3.前進后退按鈕
但是 火狐中,有個特點,有個“往返緩存”,這個緩存中不僅保存着頁面數據,還保存了DOM和JavaScript的狀態;實際上是將整個頁面都保存在了內存里。
所以此時后退按鈕不能刷新頁面。
此時可以使用 pageshow事件來觸發。,這個事件在頁面顯示時觸發,無論頁面是否來自緩存。在重新加載頁面中,pageshow會在load事件觸發后觸發;根據事件對象中的persisted來判斷是否是緩存中的頁面觸發的pageshow事件
注意這個事件給window添加。
1.3.元素滾動 scroll 系列
1.3.1. scroll 概述
scroll 翻譯過來就是滾動的,我們使用 scroll 系列的相關屬性可以動態的得到該元素的大小、滾動距離等。
1.3.2. 頁面被卷去的頭部
如果瀏覽器的高(或寬)度不足以顯示整個頁面時,會自動出現滾動條。當滾動條向下滾動時,頁面上面被隱藏掉的高度,我們就稱為頁面被卷去的頭部。滾動條在滾動時會觸發 onscroll事件。
1.3.3.案例:仿淘寶固定右側側邊欄
- 原先側邊欄是絕對定位
- 當頁面滾動到一定位置,側邊欄改為固定定位
- 頁面繼續滾動,會讓 返回頂部顯示出來
1.3.4.案例分析:
- 需要用到頁面滾動事件 scroll 因為是頁面滾動,所以事件源是document
- 滾動到某個位置,就是判斷頁面被卷去的上部值。
- 頁面被卷去的頭部:可以通過window.pageYOffset 獲得 如果是被卷去的左側window.pageXOffset
- 注意,元素被卷去的頭部是element.scrollTop , 如果是頁面被卷去的頭部 則是 window.pageYOffset
- 其實這個值 可以通過盒子的 offsetTop可以得到,如果大於等於這個值,就可以讓盒子固定定位了
//1. 獲取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offestTop 就是被卷去頭部的大小 一定要寫到滾動的外面
var bannerTop = banner.offsetTop
// 當我們側邊欄固定定位之后應該變化的數值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 獲取main 主體元素
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
// 2. 頁面滾動事件 scroll
document.addEventListener('scroll', function() {
// console.log(11);
// window.pageYOffset 頁面被卷去的頭部
// console.log(window.pageYOffset);
// 3 .當我們頁面被卷去的頭部大於等於了 172 此時 側邊欄就要改為固定定位
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 當我們頁面滾動到main盒子,就顯示 goback模塊
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
1.3.5.頁面被卷去的頭部兼容性解決方案
需要注意的是,頁面被卷去的頭部,有兼容性問題,因此被卷去的頭部通常有如下幾種寫法:
- 聲明了 DTD,使用 document.documentElement.scrollTop
- 未聲明 DTD,使用 document.body.scrollTop
- 新方法 window.pageYOffset和 window.pageXOffset,IE9 開始支持
function getScroll() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
使用的時候 getScroll().left
1.4. 三大系列總結
他們主要用法:
1.offset系列 經常用於獲得元素位置 offsetLeft offsetTop
2.client經常用於獲取元素大小 clientWidth clientHeight
3.scroll 經常用於獲取滾動距離 scrollTop scrollLeft
4.注意頁面滾動的距離通過 window.pageXOffset 獲得
1.5. mouseenter 和mouseover的區別
- 當鼠標移動到元素上時就會觸發mouseenter 事件
- 類似 mouseover,它們兩者之間的差別是
- mouseover 鼠標經過自身盒子會觸發,經過子盒子還會觸發。mouseenter 只會經過自身盒子觸發
- 之所以這樣,就是因為mouseenter不會冒泡
- 跟mouseenter搭配鼠標離開 mouseleave 同樣不會冒泡
1.6. 動畫函數封裝
1.6.1. 動畫實現原理
核心原理:通過定時器 setInterval() 不斷移動盒子位置。
實現步驟:
- 獲得盒子當前位置
- 讓盒子在當前位置加上1個移動距離
- 利用定時器不斷重復這個操作
- 加一個結束定時器的條件
- 注意此元素需要添加定位,才能使用element.style.left
1.6.2. 動畫函數給不同元素記錄不同定時器
如果多個元素都使用這個動畫函數,每次都要var 聲明定時器。我們可以給不同的元素使用不同的定時器(自己專門用自己的定時器)。
核心原理:利用 JS 是一門動態語言,可以很方便的給當前對象添加屬性。
function animate(obj, target) {
// 當我們不斷的點擊按鈕,這個元素的速度會越來越快,因為開啟了太多的定時器
// 解決方案就是 讓我們元素只有一個定時器執行
// 先清除以前的定時器,只保留當前的一個定時器執行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
if (obj.offsetLeft >= target) {
// 停止動畫 本質是停止定時器
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 + 'px';
}, 30);
}
day06 - Web APIs
學習目標:
能夠封裝簡單動畫函數
能夠理解緩動動畫的封裝
能夠使用動畫函數
能夠寫出網頁輪播圖案例
能夠寫出移動端觸屏事件
1.1. 動畫函數封裝
1.1.1 緩動效果原理
緩動動畫就是讓元素運動速度有所變化,最常見的是讓速度慢慢停下來
思路:
- 讓盒子每次移動的距離慢慢變小,速度就會慢慢落下來。
- 核心算法: (目標值 - 現在的位置) / 10 做為每次移動的距離步長
- 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
- 注意步長值需要取整
1.1.2 動畫函數多個目標值之間移動
可以讓動畫函數從 800 移動到 500。
當我們點擊按鈕時候,判斷步長是正值還是負值
1.如果是正值,則步長往大了取整
2.如果是負值,則步長 向小了取整
1.1.3 動函數添加回調函數
回調函數原理:函數可以作為一個參數。將這個函數作為參數傳到另一個函數里面,當那個函數執行完之后,再執行傳進去的這個函數,這個過程就叫做回調。
回調函數寫的位置:定時器結束的位置。
1.1.4 動畫完整版代碼:
function animate(obj, target, callback) {
// console.log(callback); callback = function() {} 調用的時候 callback()
// 先清除以前的定時器,只保留當前的一個定時器執行
clearInterval(obj.timer);
obj.timer = setInterval(function() {
// 步長值寫到定時器的里面
// 把我們步長值改為整數 不要出現小數的問題
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
// 停止動畫 本質是停止定時器
clearInterval(obj.timer);
// 回調函數寫到定時器結束里面
// if (callback) {
// // 調用函數
// callback();
// }
callback && callback();
}
// 把每次加1 這個步長值改為一個慢慢變小的值 步長公式:(目標值 - 現在的位置) / 10
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
1.2. 常見網頁特效案例
1.2.1 案例:網頁輪播圖
輪播圖也稱為焦點圖,是網頁中比較常見的網頁特效。
功能需求:
1.鼠標經過輪播圖模塊,左右按鈕顯示,離開隱藏左右按鈕。
2.點擊右側按鈕一次,圖片往左播放一張,以此類推,左側按鈕同理。
3.圖片播放的同時,下面小圓圈模塊跟隨一起變化。
4.點擊小圓圈,可以播放相應圖片。
5.鼠標不經過輪播圖,輪播圖也會自動播放圖片。
6.鼠標經過,輪播圖模塊, 自動播放停止。
window.addEventListener('load', function() {
// 1. 獲取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 2. 鼠標經過focus 就顯示隱藏左右按鈕
focus.addEventListener('mouseenter', function() {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null; // 清除定時器變量
});
focus.addEventListener('mouseleave', function() {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function() {
//手動調用點擊事件
arrow_r.click();
}, 2000);
});
// 3. 動態生成小圓圈 有幾張圖片,我就生成幾個小圓圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 創建一個小li
var li = document.createElement('li');
// 記錄當前小圓圈的索引號 通過自定義屬性來做
li.setAttribute('index', i);
// 把小li插入到ol 里面
ol.appendChild(li);
// 4. 小圓圈的排他思想 我們可以直接在生成小圓圈的同時直接綁定點擊事件
li.addEventListener('click', function() {
// 干掉所有人 把所有的小li 清除 current 類名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 當前的小li 設置current 類名
this.className = 'current';
// 5. 點擊小圓圈,移動圖片 當然移動的是 ul
// ul 的移動距離 小圓圈的索引號 乘以 圖片的寬度 注意是負值
// 當我們點擊了某個小li 就拿到當前小li 的索引號
var index = this.getAttribute('index');
// 當我們點擊了某個小li 就要把這個li 的索引號給 num
num = index;
// 當我們點擊了某個小li 就要把這個li 的索引號給 circle
circle = index;
// num = circle = index;
console.log(focusWidth);
console.log(index);
animate(ul, -index * focusWidth);
})
}
// 把ol里面的第一個小li設置類名為 current
ol.children[0].className = 'current';
// 6. 克隆第一張圖片(li)放到ul 最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 點擊右側按鈕, 圖片滾動一張
var num = 0;
// circle 控制小圓圈的播放
var circle = 0;
// flag 節流閥
var flag = true;
arrow_r.addEventListener('click', function() {
if (flag) {
flag = false; // 關閉節流閥
// 如果走到了最后復制的一張圖片,此時 我們的ul 要快速復原 left 改為 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function() {
flag = true; // 打開節流閥
});
// 8. 點擊右側按鈕,小圓圈跟隨一起變化 可以再聲明一個變量控制小圓圈的播放
circle++;
// 如果circle == 4 說明走到最后我們克隆的這張圖片了 我們就復原
if (circle == ol.children.length) {
circle = 0;
}
// 調用函數
circleChange();
}
});
// 9. 左側按鈕做法
arrow_l.addEventListener('click', function() {
if (flag) {
flag = false;
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function() {
flag = true;
});
// 點擊左側按鈕,小圓圈跟隨一起變化 可以再聲明一個變量控制小圓圈的播放
circle--;
// 如果circle < 0 說明第一張圖片,則小圓圈要改為第4個小圓圈(3)
// if (circle < 0) {
// circle = ol.children.length - 1;
// }
circle = circle < 0 ? ol.children.length - 1 : circle;
// 調用函數
circleChange();
}
});
function circleChange() {
// 先清除其余小圓圈的current類名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下當前的小圓圈的current類名
ol.children[circle].className = 'current';
}
// 10. 自動播放輪播圖
var timer = setInterval(function() {
//手動調用點擊事件
arrow_r.click();
}, 2000);
})
1.2.2. 節流閥
防止輪播圖按鈕連續點擊造成播放過快。
節流閥目的:當上一個函數動畫內容執行完畢,再去執行下一個函數動畫,讓事件無法連續觸發。
核心實現思路:利用回調函數,添加一個變量來控制,鎖住函數和解鎖函數。
開始設置一個變量var flag= true;
If(flag){flag = false; do something} 關閉水龍頭
利用回調函數動畫執行完畢, flag = true 打開水龍頭
1.2.3. 案例:返回頂部
- 帶有動畫的返回頂部
- 此時可以繼續使用我們封裝的動畫函數
- 只需要把所有的left 相關的值改為 跟 頁面垂直滾動距離相關就可以了
- 頁面滾動了多少,可以通過 window.pageYOffset 得到
- 最后是頁面滾動,使用 window.scroll(x,y)
//1. 獲取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
// banner.offestTop 就是被卷去頭部的大小 一定要寫到滾動的外面
var bannerTop = banner.offsetTop
// 當我們側邊欄固定定位之后應該變化的數值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
// 獲取main 主體元素
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
// 2. 頁面滾動事件 scroll
document.addEventListener('scroll', function() {
// console.log(11);
// window.pageYOffset 頁面被卷去的頭部
// console.log(window.pageYOffset);
// 3 .當我們頁面被卷去的頭部大於等於了 172 此時 側邊欄就要改為固定定位
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
// 4. 當我們頁面滾動到main盒子,就顯示 goback模塊
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
// 3. 當我們點擊了返回頂部模塊,就讓窗口滾動的頁面的最上方
goBack.addEventListener('click', function() {
// 里面的x和y 不跟單位的 直接寫數字即可
// window.scroll(0, 0);
// 因為是窗口滾動 所以對象是window
animate(window, 0);
});
1.2.4. 案例:筋頭雲案例
- 利用動畫函數做動畫效果
- 原先筋斗雲的起始位置是0
- 鼠標經過某個小li,把當前小li的offsetLeft 位置做為目標值即可
- 鼠標離開某個小li,就把目標值設為 0
- 如果點擊了某個小li, 就把li當前的位置存儲起來,做為筋斗雲的起始位置
window.addEventListener('load', function() {
// 1. 獲取元素
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
// 2. 給所有的小li綁定事件
// 這個current 做為筋斗雲的起始位置
var current = 0;
for (var i = 0; i < lis.length; i++) {
// (1) 鼠標經過把當前小li 的位置做為目標值
lis[i].addEventListener('mouseenter', function() {
animate(cloud, this.offsetLeft);
});
// (2) 鼠標離開就回到起始的位置
lis[i].addEventListener('mouseleave', function() {
animate(cloud, current);
});
// (3) 當我們鼠標點擊,就把當前位置做為目標值
lis[i].addEventListener('click', function() {
current = this.offsetLeft;
});
}
})
1.3. 觸屏事件
1.3.1. 觸屏事件概述
移動端瀏覽器兼容性較好,我們不需要考慮以前 JS 的兼容性問題,可以放心的使用原生 JS 書寫效果,但是移動端也有自己獨特的地方。比如觸屏事件 touch(也稱觸摸事件),Android和 IOS 都有。
touch 對象代表一個觸摸點。觸摸點可能是一根手指,也可能是一根觸摸筆。觸屏事件可響應用戶手指(或觸控筆)對屏幕或者觸控板操作。
常見的觸屏事件如下:
1.3.2. 觸摸事件對象(TouchEvent)
TouchEvent 是一類描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態變化的事件。這類事件用於描述一個或多個觸點,使開發者可以檢測觸點的移動,觸點的增加和減少,等等
touchstart、touchmove、touchend 三個事件都會各自有事件對象。
觸摸事件對象重點我們看三個常見對象列表:
因為平時我們都是給元素注冊觸摸事件,所以重點記住 targetTocuhes
1.3.3.案例:移動端拖動元素
-
touchstart、touchmove、touchend可以實現拖動元素
-
但是拖動元素需要當前手指的坐標值 我們可以使用 targetTouches[0] 里面的pageX 和 pageY
-
移動端拖動的原理: 手指移動中,計算出手指移動的距離。然后用盒子原來的位置 + 手指移動的距離
-
手指移動的距離: 手指滑動中的位置 減去 手指剛開始觸摸的位置
拖動元素三步曲:
(1) 觸摸元素 touchstart: 獲取手指初始坐標,同時獲得盒子原來的位置
(2) 移動手指 touchmove: 計算手指的滑動距離,並且移動盒子
(3) 離開手指 touchend:
注意: 手指移動也會觸發滾動屏幕所以這里要阻止默認的屏幕滾動 e.preventDefault();
day07 - Web APIs
學習目標:
能夠寫出移動端觸屏事件
能夠寫出常見的移動端特效
能夠使用移動端開發插件開發移動端特效
能夠使用移動端開發框架開發移動端特效
能夠寫出 sessionStorage 數據的存儲以及獲取
能夠寫出 localStorage 數據的存儲以及獲取
能夠說出它們兩者的區別
1.1. 觸屏事件
1.1.1 觸屏事件概述
移動端瀏覽器兼容性較好,我們不需要考慮以前 JS 的兼容性問題,可以放心的使用原生 JS 書寫效果,但是移動端也有自己獨特的地方。比如觸屏事件 touch(也稱觸摸事件),Android 和 IOS 都有。touch 對象代表一個觸摸點。觸摸點可能是一根手指,也可能是一根觸摸筆。觸屏事件可響應用戶手指(或觸控筆)對屏幕或者觸控板操作。
常見的觸屏事件如下:
1.1.2 觸摸事件對象(TouchEvent)
TouchEvent 是一類描述手指在觸摸平面(觸摸屏、觸摸板等)的狀態變化的事件。這類事件用於描述一個或多個觸點,使開發者可以檢測觸點的移動,觸點的增加和減少,等等
touchstart、touchmove、touchend 三個事件都會各自有事件對象。
觸摸事件對象重點我們看三個常見對象列表:
因為平時我們都是給元素注冊觸摸事件,所以重點記住 targetTocuhes
1.1.3 移動端拖動元素
- touchstart、touchmove、touchend 可以實現拖動元素
- 但是拖動元素需要當前手指的坐標值 我們可以使用 targetTouches[0] 里面的pageX 和 pageY
- 移動端拖動的原理: 手指移動中,計算出手指移動的距離。然后用盒子原來的位置 + 手指移動的距離
- 手指移動的距離: 手指滑動中的位置 減去 手指剛開始觸摸的位置
拖動元素三步曲:
(1) 觸摸元素 touchstart: 獲取手指初始坐標,同時獲得盒子原來的位置
(2) 移動手指 touchmove: 計算手指的滑動距離,並且移動盒子
(3) 離開手指 touchend:
注意: 手指移動也會觸發滾動屏幕所以這里要阻止默認的屏幕滾動 e.preventDefault();
1.2. 移動端常見特效
1.2.1 案例: 移動輪播圖
移動端輪播圖功能和基本PC端一致。
- 可以自動播放圖片
- 手指可以拖動播放輪播圖
1.2.2. 案例分析:
-
自動播放功能
-
開啟定時器
-
移動端移動,可以使用translate 移動
-
想要圖片優雅的移動,請添加過渡效果
-
自動播放功能-無縫滾動
-
注意,我們判斷條件是要等到圖片滾動完畢再去判斷,就是過渡完成后判斷
-
此時需要添加檢測過渡完成事件 transitionend
-
判斷條件:如果索引號等於 3 說明走到最后一張圖片,此時 索引號要復原為 0
-
此時圖片,去掉過渡效果,然后移動
-
如果索引號小於0, 說明是倒着走, 索引號等於2
-
此時圖片,去掉過渡效果,然后移動
1.2.3 classList 屬性
classList屬性是HTML5新增的一個屬性,返回元素的類名。但是ie10以上版本支持。
該屬性用於在元素中添加,移除及切換 CSS 類。有以下方法
添加類:
element.classList.add(’類名’);
focus.classList.add('current');
移除類:
element.classList.remove(’類名’);
focus.classList.remove('current');
切換類:
element.classList.toggle(’類名’);
focus.classList.toggle('current');
注意:以上方法里面,所有類名都不帶點
1.2.4. 案例分析
-
小圓點跟隨變化效果
-
把ol里面li帶有current類名的選出來去掉類名 remove
-
讓當前索引號的小li 加上 current add
-
但是,是等着過渡結束之后變化,所以這個寫到 transitionend 事件里面
-
手指滑動輪播圖
-
本質就是ul跟隨手指移動,簡單說就是移動端拖動元素
-
觸摸元素touchstart: 獲取手指初始坐標
-
移動手指touchmove: 計算手指的滑動距離,並且移動盒子
-
離開手指touchend: 根據滑動的距離分不同的情況
-
如果移動距離小於某個像素 就回彈原來位置
-
如果移動距離大於某個像素就上一張下一張滑動。
-
滑動也分為左滑動和右滑動判斷的標准是 移動距離正負 如果是負值就是左滑 反之右滑
-
如果是左滑就播放下一張 (index++)
-
如果是右滑就播放上一張 (index--)
1.3.1. 案例:返回頂部
當頁面滾動某個地方,就顯示,否則隱藏
點擊可以返回頂部
1.3.2.案例分析
- 滾動某個地方顯示
- 事件:scroll頁面滾動事件
- 如果被卷去的頭部(window.pageYOffset )大於某個數值
- 點擊,window.scroll(0,0) 返回頂部
1.4. click 延時解決方案
移動端 click 事件會有 300ms 的延時,原因是移動端屏幕雙擊會縮放(double tap to zoom) 頁面。
解決方案:
1. 禁用縮放。 瀏覽器禁用默認的雙擊縮放行為並且去掉300ms 的點擊延遲。
<meta name="viewport" content="user-scalable=no">
2.利用touch事件自己封裝這個事件解決300ms 延遲。
原理就是:
- 當我們手指觸摸屏幕,記錄當前觸摸時間
- 當我們手指離開屏幕, 用離開的時間減去觸摸的時間
- 如果時間小於150ms,並且沒有滑動過屏幕, 那么我們就定義為點擊
代碼如下:
//封裝tap,解決click 300ms 延時
function tap (obj, callback) {
var isMove = false;
var startTime = 0; // 記錄觸摸時候的時間變量
obj.addEventListener('touchstart', function (e) {
startTime = Date.now(); // 記錄觸摸時間
});
obj.addEventListener('touchmove', function (e) {
isMove = true; // 看看是否有滑動,有滑動算拖拽,不算點擊
});
obj.addEventListener('touchend', function (e) {
if (!isMove && (Date.now() - startTime) < 150) { // 如果手指觸摸和離開時間小於150ms 算點擊
callback && callback(); // 執行回調函數
}
isMove = false; // 取反 重置
startTime = 0;
});
}
//調用
tap(div, function(){ // 執行代碼 });
-
使用插件。fastclick 插件解決300ms 延遲。
1.5. 移動端常用開發插件
1.5.1. 什么是插件
移動端要求的是快速開發,所以我們經常會借助於一些插件來幫我完成操作,那么什么是插件呢?
JS 插件是 js 文件,它遵循一定規范編寫,方便程序展示效果,擁有特定功能且方便調用。如輪播圖和瀑布流插件。
特點:它一般是為了解決某個問題而專門存在,其功能單一,並且比較小。
我們以前寫的animate.js 也算一個最簡單的插件
fastclick 插件解決 300ms 延遲。 使用延時
GitHub官網地址: https://github.com/ftlabs/fastclick
1.5.2. 插件的使用
-
引入 js 插件文件。
-
按照規定語法使用。
-
fastclick 插件解決 300ms 延遲。 使用延時
-
GitHub官網地址: https://github.com/ftlabs/fastclick
if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); }
1.5.3. Swiper 插件的使用
中文官網地址: https://www.swiper.com.cn/
- 引入插件相關文件。
- 按照規定語法使用
1.5.4. 其他移動端常見插件
lsuperslide: http://www.superslide2.com/
l iscroll: https://github.com/cubiq/iscroll
1.5.5. 插件的使用總結
1.確認插件實現的功能
2.去官網查看使用說明
3.下載插件
4.打開demo實例文件,查看需要引入的相關文件,並且引入
5.復制demo實例文件中的結構html,樣式css以及js代碼
1.5.6. 移動端視頻插件 zy.media.js
H5 給我們提供了 video 標簽,但是瀏覽器的支持情況不同。
不同的視頻格式文件,我們可以通過source解決。
但是外觀樣式,還有暫停,播放,全屏等功能我們只能自己寫代碼解決。
這個時候我們可以使用插件方式來制作。
我們可以通過 JS 修改元素的大小、顏色、位置等樣式。
1.6. 移動端常用開發框架
1.6.1. 移動端視頻插件 zy.media.js
框架,顧名思義就是一套架構,它會基於自身的特點向用戶提供一套較為完整的解決方案。框架的控制權在框架本身,使用者要按照框架所規定的某種規范進行開發。
插件一般是為了解決某個問題而專門存在,其功能單一,並且比較小。
前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能開發PC端,也能開發移動端
前端常用的移動端插件有 swiper、superslide、iscroll等。
框架: 大而全,一整套解決方案
插件: 小而專一,某個功能的解決方案
1.6.2. Bootstrap
Bootstrap 是一個簡潔、直觀、強悍的前端開發框架,它讓 web 開發更迅速、簡單。
它能開發PC端,也能開發移動端
Bootstrap JS插件使用步驟:
1.引入相關js 文件
2.復制HTML 結構
3.修改對應樣式
4.修改相應JS 參數
1.7. 本地存儲
隨着互聯網的快速發展,基於網頁的應用越來越普遍,同時也變的越來越復雜,為了滿足各種各樣的需求,會經常性在本地存儲大量的數據,HTML5規范提出了相關解決方案。
1.7.1.本地存儲特性
1、數據存儲在用戶瀏覽器中
2、設置、讀取方便、甚至頁面刷新不丟失數據
3、容量較大,sessionStorage約5M、localStorage約20M
4、只能存儲字符串,可以將對象JSON.stringify() 編碼后存儲
1.7.2.window.sessionStorage
1、生命周期為關閉瀏覽器窗口
2、在同一個窗口(頁面)下數據可以共享
3、以鍵值對的形式存儲使用
存儲數據:
sessionStorage.setItem(key, value)
獲取數據:
sessionStorage.getItem(key)
刪除數據:
sessionStorage.removeItem(key)
清空數據:(所有都清除掉)
sessionStorage.clear()
1.7.3.window.localStorage
1、聲明周期永久生效,除非手動刪除 否則關閉頁面也會存在
2、可以多窗口(頁面)共享(同一瀏覽器可以共享)
3. 以鍵值對的形式存儲使用
存儲數據:
localStorage.setItem(key, value)
獲取數據:
localStorage.getItem(key)
刪除數據:
localStorage.removeItem(key)
清空數據:(所有都清除掉)
localStorage.clear()
1.7.4.案例:記住用戶名
如果勾選記住用戶名, 下次用戶打開瀏覽器,就在文本框里面自動顯示上次登錄的用戶名
案例分析
-
把數據存起來,用到本地存儲
-
關閉頁面,也可以顯示用戶名,所以用到localStorage
-
打開頁面,先判斷是否有這個用戶名,如果有,就在表單里面顯示用戶名,並且勾選復選框
-
當復選框發生改變的時候change事件
-
如果勾選,就存儲,否則就移除