s一個(gè)非常重要的作用就是對(duì)dom進(jìn)行操作,所謂的dom,可以理解為html代碼里的一個(gè)個(gè)節(jié)點(diǎn)。比如,body標(biāo)簽元素就是一個(gè)dom。本文對(duì)js的dom操作進(jìn)行一些總結(jié)。
按照慣例,先上一個(gè)剛寫好的小例子,代碼在最后給出:
現(xiàn)在,來(lái)看看js對(duì)dom的一些操作吧。
首先,給出一個(gè)html模板,接下來(lái)開始用js來(lái)做一些有趣的事情吧,css樣式的繪制就不細(xì)說(shuō)了,先上代碼:
css
*{margin: 0;padding: 0;} .parent { width: 305px; height: 302px; background: #d7e6ea; margin: 100px auto; } .parent .child { width: 80px; height: 80px; background: deepskyblue; float: left; margin-top: 10px; margin-left: 9px; margin-right: 12px; margin-bottom: 12px; box-shadow: 3px -3px 2px #9898c7; text-align: center; line-height: 80px; font-family: "微軟雅黑"; font-size: 28px; text-shadow: 2px 2px 2px yellowgreen; }
html
<body> <div class='parent'> <div class='child'>1</div> <div class='child'>2</div> <div class='child'>3</div> <div class='child'>4</div> <div class='child'>5</div> <div class='child'>6</div> <div class='child'>7</div> <div class='child'>8</div> <div class='child'>9</div> </div> </body>
效果圖
從代碼中,我們可以看到,body是一個(gè)大節(jié)點(diǎn),里面套了一個(gè)class屬性為parent的div盒子,然后我們又在這個(gè)盒子里面放了9個(gè)小盒子。
1.最簡(jiǎn)單的dom方法封裝
在本系列中,假設(shè)我們不考慮用jQuery。
現(xiàn)在給第三個(gè)盒子添加一個(gè)id。
<div id='targetBox' class='child'>3</div>
如何拿到這個(gè)盒子呢?
很顯然,最先想到的肯定是document.getElementById() 方法,于是就有了這樣的代碼。
var box=document.getElementById('targetBox'); box.style.background='#FEAF51';
效果:
當(dāng)然,我們很多時(shí)候都不希望每次都把document.getElementById(id)給寫一遍,那么,是不是可以將這一個(gè)過(guò)程封裝起來(lái)呢?
于是,自然而然的,我們會(huì)這么寫:
//獲取JavaScript的dom對(duì)象 function dom(id){ return document.getElementById(id); }; var box=dom('targetBox'); box.style.background='#FEAF51';
完美運(yùn)行,我們知道,在jQuery中,是這樣的:
var box=$('#targetBox');
那么,為了讓代碼更加山寨,不妨將dom方法稍微改進(jìn)一下嘞!
//獲取JavaScript的dom對(duì)象 function dom(id){ if(id.toString().indexOf('#') !=-1) { id=id.replace('#',''); } return document.getElementById(id); }; var box=dom('#targetBox'); box.style.background='#FEAF51';
2.如何獲取dom元素在父盒子中的位置?
剛才,我們已經(jīng)獲得了編號(hào)為3的div盒子,要得到它在父盒子的角標(biāo)位置,首先肯定要拿到它的父盒子對(duì)象吧。
像這樣:
var box=dom('#targetBox'); var parent=box.parentElement;
parent打印出來(lái)是這樣的:
看來(lái)很順利呢,接下來(lái)因?yàn)橐滥繕?biāo)元素在父盒子的位置,則需要拿到父盒子所有的孩子節(jié)點(diǎn)。
像這樣:
var children=parent.children;
打印結(jié)果:
接下來(lái)就要遍歷這些孩子節(jié)點(diǎn)啦,children 的數(shù)據(jù)類型是object。
然而,在js中我們可以遍歷數(shù)組,卻無(wú)法直接遍歷object,咋辦呢?
原來(lái),這是一個(gè)特殊的object,因?yàn)樗幸粋€(gè)length屬性。有l(wèi)ength屬性的object,可以通過(guò)以下方式轉(zhuǎn)換成數(shù)組(注:當(dāng)然,這邊也可以直接獲取獲取object中的length,然后來(lái)遍歷。):
Array.prototype.slice.call(object);
舉個(gè)例子:
var obj={length:2,0:'first',1:'second'}; objArr=Array.prototype.slice.call(obj); alert(objArr);
結(jié)果:
注1: length是幾個(gè),就轉(zhuǎn)換幾個(gè),如果你length寫1,那么只彈出first。
注2: key必須為數(shù)字,而且與數(shù)組的角標(biāo)是對(duì)應(yīng)的。
這里不深究call的的意思,我會(huì)在以后重新寫這方面的內(nèi)容。
回到正題,現(xiàn)在可以拿到數(shù)組形式的children了!
var children=Array.prototype.slice.call(parent.children);
開始遍歷:
for(var i=0,len=children.length;i < len;i++){ if(children[i]==box){ alert(i); } }
結(jié)果:
彈出來(lái)下標(biāo)是2,因?yàn)閿?shù)組下標(biāo)的起始值是從0開始的。
上面的循環(huán)結(jié)構(gòu)還欠缺了一個(gè)東西,就是一旦找到box之后,因?yàn)榧皶r(shí)退出循環(huán)才是。像這樣:
for(var i=0,len=children.length;i < len;i++){ if(children[i]==box){ alert(i); break; } }
這樣便可以一定程度地提高性能。順便附上forEach的寫法:
children.forEach(function(child,index){ if(child==box){ alert(index); return false; } });
這樣也可以,最后,將這些內(nèi)容封裝成方法,就采用forEach的方式吧!
//查找當(dāng)前dom元素在父盒子中的位置 function getIndex(dom){ var index=-1; var domArr=Array.prototype.slice.call(dom.parentElement.children); domArr.forEach(function(obj,i){ if(obj==dom){ index=i; return false; } }); return index; };
我學(xué)習(xí)js的路線就是如此,先想盡辦法把功能實(shí)現(xiàn)了,然后才開始封裝成方法。封裝的好處不言而喻,沒(méi)有人喜歡每次用到這個(gè)功能的時(shí)候,就去把實(shí)現(xiàn)代碼拷貝一份吧。
3.如何獲取parent下面指定class的元素列表?
parent盒子下面有9個(gè)孩子節(jié)點(diǎn),我們能否通過(guò)一個(gè)什么辦法獲取到這9個(gè)孩子節(jié)點(diǎn)呢?顯然,這些孩子節(jié)點(diǎn)都有一個(gè)共同的className,那么我們可以通過(guò)這個(gè)className來(lái)獲取。
IE9 + 已經(jīng)可以通過(guò)下面的方式來(lái)實(shí)現(xiàn)了:
var children=parent.getElementsByClassName('child');
效果:
IE678還是不支持的,那么,如果讓我們自己來(lái)封裝一個(gè)方法,又該如何呢?
這里提供一種思路:
1.用getElementsByTagName獲取parent元素下所有的節(jié)點(diǎn)。
2.遍歷這些節(jié)點(diǎn),比較className,如果相同,就用一個(gè)數(shù)組裝起來(lái)。
3.返回這個(gè)數(shù)組。
上代碼:
/*通過(guò)className獲取dom元素進(jìn)行過(guò)濾*/ function getClass(pid,sClass){ var aEle=dom(pid).getElementsByTagName('*'); var arrs=[]; for(var i=0;i<aEle.length;i++){ if(aEle[i].className.indexOf(sClass)!=-1){ arrs.push(aEle[i]); } } return arrs; }
最后,附上最開始小例子的源碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
*{margin: 0;padding: 0;}
body {
background: url(https://ss1.bdstatic.com/lvoZeXSm1A5BphGlnYG/skin/12.jpg?2) no-repeat;
background-size: 100% 128%;
overflow: hidden;
}
.content {
height: 600px;
width: 305px;
margin: 100px auto;
position: relative;
border-top:8px solid #ccc;
border-right:10px solid #ccc;
}
.content .screen {
height: 298px;
width: 305px;
background: #333;
position: absolute;
}
.content .screen .icon {
width: 78px;
height: 78px;
display: inline-block;
background: url(android.png) no-repeat;
background-size: 100% 100%;
position: absolute;
top: 50%;
left: 50%;
margin-top: -39px;
margin-left: -39px;
}
.parent {
width: 305px;
height: 302px;
background: #d7e6ea;
position: absolute;
bottom: 0px;
}
.parent .child {
width: 80px;
height: 80px;
background: #eee;
float: left;
margin-top: 10px;
margin-left: 9px;
margin-right: 12px;
margin-bottom: 12px;
box-shadow: 3px -3px 2px #9898c7;
text-align: center;
line-height: 80px;
font-family: "微軟雅黑";
font-size: 28px;
text-shadow: 2px 2px 2px yellowgreen;
}
.parent .child:hover {
cursor: pointer;
background: linear-gradient(#ccc,#666);
}
.putIn {
position: absolute;
width:100%;
height:60px;
line-height: 60px;
color: #fff;
bottom:0;
right: 0;/*為了讓padding起作用*/
text-align:right;
font-size: 40px;
overflow: hidden;
padding-right: 8px;
padding-bottom: 8px;
}
</style>
</head>
<body>
<div class="content">
<div class="screen">
<i class="icon"></i>
<span id="putIn" class="putIn"></span>
</div>
<div class='parent'>
<div class='child'>1</div>
<div class='child'>2</div>
<div id='targetBox' class='child'>3</div>
<div class='child'>4</div>
<div class='child'>5</div>
<div class='child'>6</div>
<div class='child'>7</div>
<div class='child'>8</div>
<div class='child'>9</div>
</div>
</div>
</body>
<script>
//獲取JavaScript的dom對(duì)象
function dom(id){
if(id.toString().indexOf('#') !=-1) {
id=id.replace('#','');
}
return document.getElementById(id);
};
var buttons=document.getElementsByClassName('child');
var putIn=dom('#putIn');
for(var i=0,len=buttons.length;i < len;i++){
buttons[i].onclick=function(){
var num=this.innerHTML;
if(putIn.innerText.length < 13 )
putIn.innerText=putIn.innerText + num;
}
}
</script>
</html>
包是JavaScript這門語(yǔ)言中一個(gè)非常強(qiáng)大的特性,它允許函數(shù)訪問(wèn)并操作函數(shù)外部的變量。在深入理解閉包之前,我們先來(lái)看看什么是作用域和詞法作用域。
在JavaScript中,每個(gè)函數(shù)都有自己的作用域。作用域可以被視為一個(gè)變量存儲(chǔ)的環(huán)境,只有在這個(gè)環(huán)境中聲明的變量和函數(shù)才可以被訪問(wèn)。詞法作用域是指在代碼編寫時(shí)函數(shù)和變量的作用域就已經(jīng)確定下來(lái),不會(huì)改變。
閉包是指那些能夠訪問(wèn)自由變量的函數(shù)。所謂自由變量,是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也不是函數(shù)局部變量的變量。閉包可以記憶并訪問(wèn)所在的詞法作用域,即使該函數(shù)在其詞法作用域之外執(zhí)行。
閉包有多種用途,包括但不限于:
下面通過(guò)幾個(gè)實(shí)際的例子來(lái)演示閉包的概念。
這個(gè)例子展示了如何使用閉包來(lái)創(chuàng)建一個(gè)私有變量count,這個(gè)變量只能通過(guò)特定的函數(shù)來(lái)訪問(wèn)和修改。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>閉包示例:計(jì)數(shù)器</title>
</head>
<body>
<button id="counterBtn">點(diǎn)擊我</button>
<p>點(diǎn)擊次數(shù):<span id="count">0</span></p>
<script>
function createCounter() {
let count=0; // 私有變量
return function() {
count +=1;
document.getElementById('count').textContent=count;
};
}
const counter=createCounter();
document.getElementById('counterBtn').addEventListener('click', counter);
</script>
</body>
</html>
在這個(gè)例子中,createCounter函數(shù)返回了一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)能夠訪問(wèn)createCounter函數(shù)作用域中的count變量。即使createCounter函數(shù)執(zhí)行完成后,這個(gè)匿名函數(shù)依然能夠訪問(wèn)到count變量,這就是閉包的作用。
閉包允許我們創(chuàng)建模塊化的代碼,我們可以將相關(guān)的功能封裝在一個(gè)函數(shù)中,只暴露必要的接口。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>閉包示例:模塊化</title>
</head>
<body>
<button id="helloBtn">問(wèn)好</button>
<button id="byeBtn">告別</button>
<script>
const greetingModule=(function() {
const name='小明'; // 私有變量
function sayHello() {
alert('你好, ' + name + '!');
}
function sayGoodbye() {
alert('再見(jiàn), ' + name + '!');
}
return {
hello: sayHello,
bye: sayGoodbye
};
})();
document.getElementById('helloBtn').addEventListener('click', greetingModule.hello);
document.getElementById('byeBtn').addEventListener('click', greetingModule.bye);
</script>
</body>
</html>
在這個(gè)例子中,我們創(chuàng)建了一個(gè)立即執(zhí)行的函數(shù)表達(dá)式(IIFE),它返回了一個(gè)對(duì)象,這個(gè)對(duì)象包含兩個(gè)方法:sayHello和sayGoodbye。這兩個(gè)方法都可以訪問(wèn)到私有變量name,但是外部代碼卻無(wú)法直接訪問(wèn)name變量。這樣我們就創(chuàng)建了一個(gè)簡(jiǎn)單的模塊,它封裝了私有數(shù)據(jù)和公共接口。
經(jīng)典的面試題之一是在循環(huán)中使用閉包。下面的例子演示了如何正確地在循環(huán)中創(chuàng)建閉包。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>閉包示例:循環(huán)中的閉包</title>
</head>
<body>
<script>
function createButtons() {
for (var i=1; i <=5; i++) {
let button=document.createElement('button');
button.textContent='按鈕 ' + i;
button.addEventListener('click', (function(number) {
return function() {
alert('這是按鈕 ' + number);
};
})(i));
document.body.appendChild(button);
}
}
createButtons();
</script>
</body>
</html>
在這個(gè)例子中,我們?cè)谘h(huán)中創(chuàng)建了五個(gè)按鈕,并為每個(gè)按鈕添加了點(diǎn)擊事件監(jiān)聽(tīng)器。注意我們是如何使用立即執(zhí)行的函數(shù)表達(dá)式來(lái)創(chuàng)建閉包的,它捕獲了當(dāng)前的i值,并使每個(gè)按鈕都能彈出正確的編號(hào)。
閉包是JavaScript中一個(gè)非常強(qiáng)大的特性,它允許我們以優(yōu)雅的方式封裝和管理數(shù)據(jù)和功能。理解閉包對(duì)于成為一名高效的前端工程師至關(guān)重要。以上例子僅僅是閉包用途的冰山一角,但它們展示了閉包的基本概念和一些常見(jiàn)的應(yīng)用場(chǎng)景。掌握閉包,你將能夠編寫出更加模塊化、高效和可維護(hù)的代碼。
件是 Web 開發(fā)的方向,現(xiàn)在的熱點(diǎn)是 JavaScript 組件,但是 HTML 組件未來(lái)可能更有希望。 本文就介紹 HTML 組件的基礎(chǔ)知識(shí):自定義元素(custom elements)。
一、瀏覽器處理
我們一般都使用標(biāo)準(zhǔn)的 HTML 元素。
<p>Hello World</p>
上面代碼中,<p>就是標(biāo)準(zhǔn)的 HTML 元素。
如果使用非標(biāo)準(zhǔn)的自定義元素,會(huì)有什么結(jié)果?
<greeting>Hello World</greeting>
上面代碼中,<greeting>就是非標(biāo)準(zhǔn)元素,瀏覽器不認(rèn)識(shí)它。這段代碼的運(yùn)行結(jié)果是,瀏覽器照常顯示Hello World,這說(shuō)明瀏覽器并沒(méi)有過(guò)濾這個(gè)元素。
現(xiàn)在,為自定義元素加上樣式。
greeting { display: block; font-size: 36px; color: red; }
運(yùn)行結(jié)果如下。
接著,使用腳本操作這個(gè)元素。
function customTag(tagName, fn){ Array .from(document.getElementsByTagName(tagName)) .forEach(fn); } function greetingHandler(element) { element.innerHTML='你好,世界'; } customTag('greeting', greetingHandler);
運(yùn)行結(jié)果如下。
這說(shuō)明,瀏覽器對(duì)待自定義元素,就像對(duì)待標(biāo)準(zhǔn)元素一樣,只是沒(méi)有默認(rèn)的樣式和行為。這種處理方式是寫入 HTML5 標(biāo)準(zhǔn)的。
“User agents must treat elements and attributes that they do not understand as semantically neutral; leaving them in the DOM (for DOM processors), and styling them according to CSS (for CSS processors), but not inferring any meaning from them.”
上面這段話的意思是,瀏覽器必須將自定義元素保留在 DOM 之中,但不會(huì)任何語(yǔ)義。除此之外,自定義元素與標(biāo)準(zhǔn)元素都一致。
事實(shí)上,瀏覽器提供了一個(gè)HTMLUnknownElement對(duì)象,所有自定義元素都是該對(duì)象的實(shí)例。
var tabs=document.createElement('tabs'); tabs instanceof HTMLUnknownElement // true tabs instanceof HTMLElement // true
上面代碼中,tabs是一個(gè)自定義元素,同時(shí)繼承了HTMLUnknownElement和HTMLElement接口。
獲取方式:
1.在你手機(jī)的右上角有【關(guān)注】選項(xiàng),或點(diǎn)擊我的頭像,點(diǎn)擊關(guān)注!(關(guān)注我)
2.關(guān)注后,手機(jī)客戶端點(diǎn)擊我的主頁(yè)面,右上角有私信,請(qǐng)私信發(fā)我:編程
電腦已經(jīng)設(shè)置好了關(guān)鍵詞自動(dòng)回復(fù),自動(dòng)領(lǐng)取就好了!這幾天上萬(wàn)個(gè)消息,真的回復(fù)不過(guò)來(lái),所以回復(fù)的時(shí)候請(qǐng)注意關(guān)鍵詞!
其實(shí)做為一個(gè)開發(fā)者,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要這里請(qǐng)私信我“編程”不管你是小白還是大牛歡迎入住大家一起交流成長(zhǎng)。小編會(huì)在里面不定期分享干貨源碼,包括我精心整理的一份零基礎(chǔ)教程。歡迎各位感興趣的的小伙伴。
學(xué)習(xí)思路:
二、HTML import
有了自定義元素,就可以寫出語(yǔ)義性非常好的 HTML 代碼。
<share-buttons> <social-button type="weibo"> <a href="...">微博</a> </social-button> <social-button type="weixin"> <a href="...">微信</a> </social-button> </share-buttons>
上面的代碼,一眼就能看出語(yǔ)義。
如果將<share-buttons>元素的樣式與腳本,封裝在一個(gè) HTML 文件share-buttons.html之中,這個(gè)元素就可以復(fù)用了。
使用的時(shí)候,先引入share-buttons.html。
<link rel="import" href="share-buttons.html">
然后,就可以在網(wǎng)頁(yè)中使用<share-buttons>了。
<article> <h1>Title</h1> <share-buttons/> ... ... </article>
HTML imports 的更多用法可以參考教程(1,2)。目前只有 Chrome 瀏覽器支持這個(gè)語(yǔ)法。
三、Custom Elements 標(biāo)準(zhǔn)
HTML5 標(biāo)準(zhǔn)規(guī)定了自定義元素是合法的。然后,W3C 就為自定義元素制定了一個(gè)單獨(dú)的 Custom Elements 標(biāo)準(zhǔn)。
它與其他三個(gè)標(biāo)準(zhǔn)放在一起—- HTML Imports,HTML Template、Shadow DOM—-統(tǒng)稱為 Web Components 規(guī)范。目前,這個(gè)規(guī)范只有 Chrome 瀏覽器支持。
Custom Elements 標(biāo)準(zhǔn)對(duì)自定義元素的名字做了限制。
“自定義元素的名字必須包含一個(gè)破折號(hào)(-)所以<x-tags>、<my-element>和<my-awesome-app>都是正確的名字,而<tabs>和<foo_bar>是不正確的。這樣的限制使得 HTML 解析器可以分辨那些是標(biāo)準(zhǔn)元素,哪些是自定義元素。”
注意,一旦名字之中使用了破折號(hào),自定義元素就不是HTMLUnknownElement的實(shí)例了。
var xTabs=document.createElement('x-tabs'); xTabs instanceof HTMLUnknownElement // false xTabs instanceof HTMLElement // true
Custom Elements 標(biāo)準(zhǔn)規(guī)定了,自定義元素的定義可以使用 ES6 的class語(yǔ)法。
// 定義一個(gè) <my-element></my-element> class MyElement extends HTMLElement {...} window.customElements.define('my-element', MyElement);
上面代碼中,原生的window.customElements對(duì)象的define方法用來(lái)定義 Custom Element。該方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)是自定義元素的名字,第二個(gè)參數(shù)是一個(gè) ES6 的class。
這個(gè)class使用get和set方法定義 Custom Element 的某個(gè)屬性。
class MyElement extends HTMLElement { get content() { return this.getAttribute('content'); } set content(val) { this.setAttribute('content', val); } }
有了這個(gè)定義,網(wǎng)頁(yè)之中就可以插入<my-element>了。
<my-element content="Custom Element"> Hello </my-element>
處理腳本如下。
function customTag(tagName, fn){ Array .from(document.getElementsByTagName(tagName)) .forEach(fn); } function myElementHandler(element) { element.textConent=element.content; } customTag('my-element', myElementHandler);
運(yùn)行結(jié)果如下。
ES6 Class 的一個(gè)好處是,可以很容易地寫出繼承類。
class MyNewElement extends MyElement { // ... } customElements.define('my-new-element', MyNewElement);
今天的教程就到這里,更多用法請(qǐng)參考谷歌的官方教程。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。