1. 程式人生 > >中高階前端應該必會,js實現事件委託代理、切換樣式、元素獲取相對於文件位置等

中高階前端應該必會,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:元素左邊到視窗左邊的距離;

  如果你看過 lazyImg 圖片的懶載入,你們就會發現,他的實現原理就是這個。

  當它圖片出現在視窗的檢視中,就會載入真的圖片資源。 

  後續繼續新增常用的、容易忘的一些 js 功能。