本篇為 JavaScript 系列筆記第八篇,將陸續更新后續內容,參考:黑馬程式員JavaScript核心教程,前端基礎教程
系列筆記:
JavaScript(一)—— 初識JavaScript / 注釋 / 輸入輸出陳述句 / 變數 / 資料型別
JavaScript(二)—— 運算子 / 流程控制 / 陣列
JavaScript(三)—— 函式 / 作用域 / 預決議 / 物件
JavaScript(四)—— 內置物件 / 簡單資料型別與復雜型別
JavaScript(五)—— Web APIs 簡介 / JavaScript 必須掌握的 DOM 操作
JavaScript(六)—— DOM 事件高級
JavaScript(七)—— BOM 瀏覽器物件模型
文章預覽

「一」元素偏移量 offset
offset 系列相關屬性可以 動態的 得到該元素的位置(偏移)、大小等:
- 獲得元素距離帶有定位父元素的位置
- 獲得元素自身的大小(寬度、高度)
- 回傳的數值不帶單位
offset 系列常用屬性如下:

offsetTop、offsetLeft屬性

- 父親有定位,則以 父親 為準,回傳 45
- 父親無定位,則以 body 為準,回傳 195
offsetWidth、offsetHeight屬性
- 計算數值時包含
padding、border、width - 可以動態獲取數值,比如瀏覽器縮小時,數值相應變化
offsetParent屬性

- 回傳帶有定位的父親,否則回傳 body
- 與節點操作中
parentNode相比,parentNode回傳最近一級的父親,無論父親是否有定位
- offset 與 style 區別
offset
- offset 可以得到任意樣式表中的樣式值
- offset 系列獲得的數值是沒有單位的
- offsetWidth 包含 padding、border、width
- offsetWidth 等屬性是只讀屬性,只能獲取不能賦值
- 只想獲取元素大小位置,offset 更合適
style
- style 只能得到行內樣式表中的樣式值
- style.width 獲得的是帶有單位的字串
- style.width 獲得不包含 padding 和 border 的值
- style.width 是可讀寫屬性,可以獲取也可以賦值
- 要想給元素更改值,需要用 style

- 案例:獲取滑鼠在盒子內的坐標

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;
})
box.addEventListener('mouseout', function () {
this.innerHTML = '';
})
- 案例:模態框拖拽

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');
// 顯示
link.addEventListener('click', function () {
mask.style.display = 'block';
login.style.display = 'block';
})
// 隱藏
closeBtn.addEventListener('click', function () {
mask.style.display = 'none';
login.style.display = 'none';
})
// 拖拽
title.addEventListener('mousedown', function (e) {
var x = e.pageX - login.offsetLeft;
var y = e.pageY - login.offsetTop;
document.addEventListener('mousemove', move);
function move(e) {
login.style.left = e.pageX - x + 'px';
login.style.top = e.pageY - y + 'px';
}
document.addEventListener('mouseup', function () {
document.removeEventListener('mousemove', move);
})
})
- 滑鼠按下
mousedown,獲取滑鼠在盒子中坐標 - 滑鼠移動
mousemove,求得模態框的left、top - 滑鼠彈起
mouseup,移除注冊事件removeEventListener
- 案例:仿京動放大鏡

部分 HTML 代碼
<div class="preview_img">
<img src="img/mac_small.jpg" alt=""> <--小圖-->
<div class="mask"></div> <--遮罩-->
<div class="preview_img_big"> <--大圖-->
<img src="img/mac_big.jpg" alt="" class="big_img">
</div>
</div>
部分 CSS 代碼
.preview_img {
position: relative;
height: 450px;
border: 1px solid #ccc;
}
.mask {
display: none;
position: absolute;
left: 0;
top: 0;
width: 300px;
height: 300px;
background: #FEDE4F;
opacity: .5; /* 不透明度 */
border: 1px solid #ccc;
cursor: move; /* 滑鼠樣式為移動 */
}
.preview_img_big {
display: none;
position: absolute;
left: 450px;
top: -1px;
width: 540px;
height: 540px;
border: 1px solid #ccc;
overflow: hidden;
}
.big_img {
position: absolute;
top: 0;
left: 0;
}
JS 代碼
window.addEventListener('load', function () {
var preview_img = document.querySelector('.preview_img');
var mask = document.querySelector('.mask');
var big = document.querySelector('.preview_img_big');
preview_img.addEventListener('mouseover', function () {
mask.style.display = 'block';
big.style.display = 'block';
})
preview_img.addEventListener('mousemove', move);
function move(e) {
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
// 減去遮罩盒子高度一半
var maskX = x - mask.offsetWidth / 2;
var maskY = y - mask.offsetHeight / 2;
// 最大移動距離(寬高相等)
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';
// 獲得大圖片
var big_img = document.querySelector('.big_img');
// 最大移動距離
var bigMax = big_img.offsetWidth - big.offsetWidth;
var bigX = maskX * bigMax / maskMax;
var bigY = maskY * bigMax / maskMax;
big_img.style.left = -bigX + 'px';
big_img.style.top = -bigY + 'px';
}
preview_img.addEventListener('mouseout', function () {
mask.style.display = 'none';
big.style.display = 'none';
})
})
注意:
- 若將 js 檔案放在 html 上面,注意一定要先加載視窗
window.addEventListener('load', function(){}),否則注冊事件系結為空,出現報錯

- 理解遮罩層和放大圖片移動的演算法
「二」元素可視區 client
通過 client 相關屬性可以動態的得到該元素的邊框大小、元素大小

- client 系列和 offset 系列區別

「三」元素滾動 scroll
使用 scroll 相關屬性可以動態得到該元素的大小、滾動距離等


onscroll事件
如果瀏覽器的高(或寬)度不足以顯示整個頁面時,會自動出現滾動條,當滾動條向下滾動時,會觸發 onscroll 事件
- 案例:仿淘寶固定右側側邊欄

部分 HTML 代碼

JS 代碼
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
var bannerTop = banner.offsetTop;
var sliderbarTop = sliderbar.offsetTop - bannerTop;
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
document.addEventListener('scroll', function (e) {
if (window.pageYOffset >= bannerTop) {
sliderbar.style.position = 'fixed';
sliderbar.style.top = sliderbarTop + 'px';
} else {
sliderbar.style.position = 'absolute';
sliderbar.style.top = '300px';
}
if (window.pageYOffset >= mainTop) {
goBack.style.display = 'block';
} else {
goBack.style.display = 'none';
}
})
注意:頁面被卷去的頭部,有兼容問題,通常有下面幾種寫法:
- 宣告了 DTD(
<!DOCTYPE html>),使用document.documentElement.scrollTop - 未宣告 DTD,使用
document.body.scrollTop - IE9 開始,新方法
window.pageYOffset、window.pageXOffset
「四」影片函式
- 影片實作原理
通過定時器 setInterval() 不斷移動盒子位置,實作步驟:
- 獲得盒子當前位置
- 通過定時器不斷重復移動單位距離(需要添加定位,使用
element.style.left) - 添加結束定時條件


- 影片函式的封裝
可能一個頁面中會多次呼叫影片程序,因此可以將其封裝成函式,
注意:函式需要傳遞 2 個引數,影片物件 和 移動的距離
function animate(obj, target) {
var timer = setInterval(function () {
if (obj.offsetLeft >= target) {
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 2 + 'px';
}, 10)
}
- 影片函式給不同元素記錄不同定時器
若每次呼叫都宣告 var timer 變數,會造成占用大量記憶體以及重復命名歧義等問題,因此,這里利用給物件添加屬性的方式進行賦值操作 obj.timer,實作了不同元素指定不同定時器


- 為避免持續點擊導致開啟多個定時器,應在函式呼叫開始時清除所有定時器
- 為避免停止后點擊還會少量移動問題,在到達指定距離后,
return結束函式呼叫
- 緩動效果原理
緩動影片就是讓元素運動速度有所變化,最常見的是逐漸降速到停止,使得效果更加自然
演算法: 步長 = (目標位置 - 當前位置)/ 10
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);


- 避免小數問題導致最后無法運動到指定
target, 這里利用了向上取整 - 此外,考慮到后退程序,向上取整也出現了問題,因此需要分類來討論

- 影片函式添加回呼函式
回呼函式原理:函式可以作為一個引數,作為傳遞到另一個函式中
function animate(obj, target, callback) {} // callback 回呼函式
animate(span, 500, function () {});


- 影片函式封裝到單獨 JS 檔案中
以后會經常使用影片函式,因此可以將其單獨封裝到一個 JS 檔案中,使用時直接參考 JS 檔案即可

- 案例:滑動盒子

<div class="sliderbar">
<span>←</span>
<div class="con">問題反饋</div>
</div>
<script>
var sliderbar = document.querySelector('.sliderbar');
var con = document.querySelector('.con');
sliderbar.addEventListener('mouseenter', function () {
animate(con, -160, function () {
sliderbar.children[0].innerHTML = '→';
});
})
sliderbar.addEventListener('mouseleave', function () {
animate(con, 0, function () {
sliderbar.children[0].innerHTML = '←';
});
})
</script>
mouseenter、mouseleave和mouseover、mouseout用法一樣,區別在于前者無法冒泡- 注意要參考 animate.js
「五」常見網頁特效案例
- 案例:淘寶輪播圖

JS 代碼主要利用

- animate.js 代碼
function animate(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
callback && callback();
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 15);
}
- index.js 代碼
window.addEventListener('load', function () {
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;
// 滑鼠經過 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);
})
// 動態生成小圓圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
for (var i = 0; i < ul.children.length; i++) {
var li = document.createElement('li');
// 淘寶原始碼中 li 中又創建了 a,其實在這里不創建 a 也可以,本案例暫且依淘寶為準
var a = document.createElement('a');
// 記錄索引
a.setAttribute('index', i);
ol.appendChild(li);
li.appendChild(a);
// 圓圈系結事件
li.addEventListener('focus', function () {
this.blur();
})
a.addEventListener('click', function () {
// 排他思想,被點擊的圓圈設定 current 樣式
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].firstChild.className = '';
}
this.className = 'current';
// 移動圖片
var index = this.getAttribute('index');
animate(ul, - index * focusWidth);
// 修改索引
num = circle = index;
})
}
ol.children[0].firstChild.className = 'current';
// 克隆第一張圖片放在 ul 最后,實作無縫滾動(不復制直接添加的話會多出一個小圓圈)
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 點擊右側按鈕, 圖片滾動一張
var num = 0;
// circle 控制小圓圈的播放
var circle = 0;
// flag 節流閥, 防止連續點擊造成過快播放
var flag = true;
// 右側按鈕
arrow_r.addEventListener('click', function () {
if (flag) {
flag = false; // 關閉節流閥
// 如果走到最后復制的一張圖片,ul 快速復原
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth, function () {
flag = true; // 打開節流閥
});
circle++;
// 最后一張時復原
if (circle == ol.children.length) {
circle = 0;
}
// 呼叫函式
circleChange();
}
})
// 左側按鈕
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 = circle < 0 ? ol.children.length - 1 : circle;
circleChange();
}
})
// 圓圈變色
function circleChange() {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].firstChild.className = '';
}
ol.children[circle].firstChild.className = 'current';
}
// 自動播放輪播圖
var timer = this.setInterval(function () {
arrow_r.click();
}, 2000);
})
- html 代碼、css 代碼由于篇幅問題不再給出
遇到的問題
- 開始寫輪播圖效果時,遇到點擊 li 后出現游標持續閃爍的問題,嘗試了各種方法,都沒有很好的解決
如上圖所示輸入游標- 最后靈機一動,發現其實這個問題只需要給整個盒子設定 font-size: 0;,即可完美解決
- 案例:影片回傳頁面頂部
在此前案例中,回傳頂部使用的是錨點鏈接,在此處介紹一種新方式:
window.scroll(x, y) // 滾動視窗至檔案中的指定位置
本案例利用 window.scroll(x, y) 配合影片函式 animate 實作影片回傳頂部效果

部分 JS 代碼
goBack.addEventListener('click', function () {
// 視窗進行滾動, 物件是 window
animate(window, 0);
// window.scroll(0, 0);
});
// 影片函式
function animate(obj, target) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var step = (target - window.pageYOffset) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (window.pageYOffset == target) {
clearInterval(obj.timer);
}
window.scroll(0, window.pageYOffset + step);
}, 15);
}
這里注意 animate 函式 將引數改為垂直相關,利用 window.pageYOffset
- 案例:筋斗云導航

window.addEventListener('load', function () {
var cloud = document.querySelector('.cloud');
var c_nav = document.querySelector('.c-nav');
var lis = c_nav.querySelectorAll('li');
// 設定變數,記錄起始位置
var current = 0;
for (var i = 0; i < lis.length; i++) {
// 滑鼠進入,當前 li 位置為目標值
lis[i].addEventListener('mouseenter', function () {
animate(cloud, this.offsetLeft);
})
// 滑鼠離開,回到起始位置
lis[i].addEventListener('mouseleave', function () {
animate(cloud, current);
})
// 滑鼠點擊,當前位置設為目標值
lis[i].addEventListener('click', function () {
current = this.offsetLeft;
})
}
})
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/307333.html
標籤:其他
上一篇:npm publish 時提示需要升級TLS 1.2的解決方案
下一篇:Vue3.0的多種偵聽方式

