中高階前端應該必會,js實現事件委託代理、切換樣式、元素獲取相對於文件位置等
1、介紹
隨著元件開發大流行,現在三大框架已經基本佔領了整個前端。
這時候,我們要是引入一個 jq 是不是先得你的專案非常臃腫,jq 也很不適合。
這個時候,你就需要來增加你 js 的功底。
2、各種操作
1、事件委託
案例分析:
<ul id= "list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul>
如上面的頁面機構,我們需要個每一個 li 新增同一個事件。
常規操作:
選擇出所有的 li 標籤,然後為所有的標籤都新增這個事件。
var liList = document.querySelectorAll('#list li') liList.foreach(function(item,index)=>{ item.addEventListener('click',doSomething) })
很簡單,也是非常好的。
但是當這裡的 li 標籤多了之後,那麼你獲取所有的 li 標籤並繫結事件就會變慢,也就是說效能就會不好了。
這個時候該如何處理呢?
事件委託:
解釋下事件委託吧,可能新手不知道。
就是把子元素想要繫結的事件,繫結到子元素的父元素上面。
然後當事件觸發的時候,通過事件冒泡來獲取到當前事件源對應的的元素。(事件冒泡和捕獲如果不知道還是需要補習下的)
程式碼展示:
var liList = document.querySelectorAll('#list') liList.addEventListener('click',function(e)=>{ if(e.target && e.target.nodeName.toUpperCase == 'LI'){ console.log('你點選了'+e.target.innerText ) } })
給一個比較完整的例子,沒有驗證...
function delegate(element, eventName, name, func) { element.addEventListener(eventName, function (e) { var target = e.target, parent = target; while (target && parent != element) { if (parent.nodeName.toLowerCase() == name) { e.target = parent func.apply(parent,e); break; } parent = parent.parentNode; } }); }
上面的例子,name 欄位可以是 類命,id 的值,也可以是 標籤。
自行修改,更靈活。。
總結:
事件代理委託主要是通過給父元素繫結事件。
通過事件的冒泡來確定當前的事件源。
確定事件源,並執行具柄。
2、es5 的元素獲取、class 的操作
es5 的元素獲取
在之前我一直都是使用的 es3 做元素獲取的,如:
document.getElementById(id) document.getElementsByClassName(class)
上面的相信是大家以前最熟悉的 js 獲取方法。
但是在 es5 出來後,你會驚奇的覺得 Jquery 選擇器可以被替代了
document.querySelector(selector) document.querySelectorAll(selector) // 例如 document.querySelector('.classs p') // 獲取 class 類下面的 p 標籤 // 可以看到和 jquery 選擇器差不多,只是功能精簡了
上面很清楚的可以知道:
querySelector 是獲取單個元素
querySelectorAll 是獲取多個匹配元素
最大的變化就是 selector。
它可以是 .className 、 #id 、也可以是多級選擇 .className p
class 類的一些 es5 操作
Dom 的 classList 屬性:
<div id = "test" class = "red big hot"></div>
獲取樣式類列表
document.querySelector('test').classList
這裡會返回一個 TokenList 也就是是個類陣列:
{ 0:red, 1:big, 2:hot, length:3, value:'red big hot' }
新增類名
document.querySelector('test').classList.add('good','new')
新增類 good , new 。如果類名已經存在,則不新增
移除某類
document.querySelector('test').classList.remove('good','new')
移除 good , new 類。移除不存在的類,會報錯
切換類
document.querySelector('test').classList.toggle('good')
切換 good 類。如果 good 存在返回true,否則false
判斷是否存在某類
document.querySelector('test').classList.contains('good')
返回 boolean 值。
3、獲取元素在父元素中第幾個
其實這個在現在的元件模式中很容易實現。
但是 js 中是如是實現的呢 ?
<ul> <li>1</li> <li>2</li> <li>3</li> </ul>
獲取方法及其封裝:
function index(parent,son) { return [].indexOf.call(parent.children,son); }
如上面:
parent 是父元素,son 很顯然是子元素
4、元素相對於文件 / 視窗檢視的位置
相對於文件的位置
在 jquery 中我們實現的方法是 $(s).offset() 來回去相對於文件的位置。
那 js 中如何實現的呢?
程式碼:
const getDocPosition = (element) => { let eleCom = element; if (typeof element === 'string') eleCom = document.querySelector(eleCom); let x = eleCom.offsetLeft; let y = eleCom.offsetTop; let parent = eleCom.offsetParent; while (parent) { x += parent.offsetLeft; y += parent.offsetTop; parent = parent.offsetParent; } return { x, y, }; };
程式碼很簡單:
可以看出,通過不斷的獲取 offsetLeft / offsetTop ,並且與它的父元素相加。
直到相加到頂級元素為止。
相對於視窗檢視位置
getBoundingClientRect用於獲取某個元素相對於視窗的位置集合。集合中有top, right, bottom, left等屬性。
rectObject = object.getBoundingClientRect();
rectObject.top:元素上邊到視窗上邊的距離;
rectObject.right:元素右邊到視窗左邊的距離;
rectObject.bottom:元素下邊到視窗上邊的距離;
rectObject.left:元素左邊到視窗左邊的距離;