JavaScript DOM

查询元素
var dom=document;

document得到dom对象

var banner=document.getElementById('page-banner');

查找id="page-banner"的元素

var titles= document.getElementsByClassName('title');

查找class="title"的所有元素,返回一个list,titles[0]表示列表第一个元素

var lis = document.getElementsByTagName('li');

按照标签查找元素,返回一个list

循环遍历元素
for (let i = 0; i < titles.length; i++) {
  console.log(titles[i]);
}
Array.from(titles).forEach(function (item) {
  console.log(item);
});

forEach是js中循环遍历数组常用方法,但不能直接用于遍历dom集合,Array.from()可以将dom集合变化为普通数组

css选择器
const wrap = document.querySelector("#book-list li:nth-child(2) .name");

用css语法选择dom元素,注意:以上方法只返回匹配的第一个元素

var books = document.querySelectorAll("#book-list li .name");

返回匹配元素的集合

获取文本
var books = document.querySelectorAll("#book-list li .name");
Array.from(books).forEach(function (book) {
  console.log(book.textContent);
});

.textContent获取元素的文本

修改文本
var books = document.querySelectorAll("#book-list li .name");
Array.from(books).forEach(function (book) {
  book.textContent = "修改后的文本";
});

.textContent在等号左边表示修改文本

innerHTML
const bookList = document.querySelector("#book-list");
bookList.innerHTML = "<h2>more...</h2>";

.innerHTML可以修改dom元素

结点类型
const banner = document.querySelector("#page-banner");
console.log("#page-banner node type is ", banner.nodeType);

.nodeType返回节点的类型,用数字表示。以下是对照表https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType

结点名字
const banner = document.querySelector("#page-banner");
console.log("#page-banner node name is ", banner.nodeName);
// #page-banner node name is  DIV
是否有子结点
const banner = document.querySelector("#page-banner");
console.log("#page-banner has child nodes", banner.hasChildNodes());
// true

返回bool值

拷贝结点
const banner = document.querySelector("#page-banner");
const clonedBanner = banner.cloneNode(true);
console.log(clonedBanner);
//<div id="page-banner">
//  <h1 class="title">Bookorama</h1>
//  <p>Books for Ninjas</p>
//  <form id="search-books">
//    <input type="text" //placeholder="Search books..." />
//  </form>
//</div>

banner.cloneNode(true)为深拷贝,拷贝该结点及其子结点,改为false则只拷贝当前结点

获取当前结点的父结点
  • parentNode
const bookList = document.querySelector("#book-list");
console.log("the parent is :", bookList.parentNode);
//the parent is : <div id=​"wrapper">​…​</div>​<header>​…​</header>​<div id=​"book-list">​…​</div>​<form id=​"add-book">​…​</form>​</div>​
  • parentElement
const bookList = document.querySelector("#book-list");
console.log("the parent is :", bookList.parentElement);

两者在大部分情况下是相同的,在没有父元素时不同
document.documentElement.parentNode; // the document node
document.documentElement.parentElement; // null

获取子结点
const bookList = document.querySelector("#book-list");
console.log(bookList.childNodes);
// [text, h2.title, text, ul, text]

.childNodes返回子结点组成的数组,结点中包括换行符

const bookList = document.querySelector("#book-list");
console.log(bookList.children);

.children获取除了换行符的子结点

获取兄弟结点
const bookList = document.querySelector("#book-list");
console.log(bookList.nextSibling);
console.log(bookList.nextElementSibling);

.nextSibling.nextElementSibling都返回相邻兄弟结点,后者不包含换行符。此外获取前面兄弟结点有.previousSibling.previousElementSibling

事件
文档加载完毕触发事件
<body onload="checkCookies()"></body>

onload=在文档加载完执行

监听按钮点击
<button onclick="changeColor(this)">click to change</button>

onclick=设置点击事件处理函数

文本输入事件
<input type="text" oninput="textChange()" />

oninput=在文本输入时触发

鼠标事件
<div onmouseover="mOver(this)" onmouseout="mOut(this)">Mouse Over Me</div>

在鼠标悬浮和离开时触发

小结:设置监听器的三种方式

方法一:
<button onclick="changeColor(this)">click to change</button>
方法二:
element.addEventListener("event", function, useCapture);
方法三:
element.onclick=function(){}

键盘事件
// filter books
const searchBar = document.forms["search-books"].querySelector("input");
searchBar.addEventListener("keyup", (e) => {
  const term = e.target.value.toLowerCase();
  const books = list.getElementsByTagName("li");
  Array.from(books).forEach((book) => {
    const title = book.firstElementChild.textContent;
    if (title.toLowerCase().indexOf(term) != -1) {
      book.style.display = "block";
    } else {
      book.style.display = "none";
    }
  });
});

"keyup"代表键盘事件,.firstElementChild得到第一个子元素

// tabbed content
const tabs = document.querySelector(".tabs");
const panels = document.querySelectorAll(".panel");
tabs.addEventListener("click", (e) => {
  if (e.target.tagName == "LI") {
    const targetPanel = document.querySelector(e.target.dataset.target);
    Array.from(panels).forEach((panel) => {
      if (panel == targetPanel) {
        panel.classList.add("active");
      } else {
        panel.classList.remove("active");
      }
    });
  }
});
监听事件删除结点
var btns = document.querySelectorAll("#book-list .delete");
Array.from(btns).forEach(function (btn) {
  btn.addEventListener("click", function (e) {
    const li = e.target.parentElement;
    li.parentElement.removeChild(li);
  });
});

e.target是触发事件的元素,removeChild(li)指定删除的结点

阻止默认事件
const link = document.querySelector("#page-banner a");
link.addEventListener("click", function (e) {
  e.preventDefault();
});

阻止a元素跳转事件

Event Bubbling
const list = document.querySelector("#book-list ul");
list.addEventListener("click", function (e) {
  if (e.target.className == "delete") {
    const li = e.target.parentElement;
    list.removeChild(li);
  }
});

监听每个li元素效率较低,较好的做法是监听它们父元素

submit事件
var forms = document.forms;

document.forms返回所有表单的列表,可以通过id或者下标获得单个form元素,如:forms[0]forms['add-book']
点击提交按钮后会刷新页面,监听提交事件(submit)可以阻止页面刷新

const addForm = document.forms["add-book"];
addForm.addEventListener("submit", function (e) {
  e.preventDefault();
  const value = addForm.querySelector("input[type='text']").value;
  console.log(value);
});

.value获得input的值

屏幕滚动事件

window.onscroll = function (){}监听屏幕滚动

属性 说明
document.documentElement.offsetHeight html可视区高度
element.offsetHeight 元素的css高度 (自身高度、边框、内边距和元素的水平滚动条),不包括伪元素,隐藏元素为0
element.offsetWidth 元素css宽度(自身宽度height、边框、内边距、竖直方向滚动条)
element.clientHeight 元素内部高度(自身高度height、内边距,不包括滚动条、边框、外边距)。内联元素和没有css样式的为0
element.clientWidth 元素内部宽度(自身高度、内边距,不包括滚动条、边框、外边距)。内联元素和没有css样式的为0
document.documentElement.scrollTop 文档顶部到可视区顶部距离(文档向上超出可视区高度)
element.scrollTop 元素顶部到可视区顶部距离
document.body.scrollHeight body元素在没有滚动条时高度
element.scrollHeight 元素在没有滚动条下高度
window.innerHeight 窗口可视区高度
window.innerWidth 窗口可视区宽度
判断元素是否滚动到底
element.scrollHeight - element.scrollTop === element.clientHeight
DOM操作

添加元素到dom中注意三项内容:dom元素、样式、内容

添加元素
const addForm = document.forms["add-book"];
addForm.addEventListener("submit", function (e) {
  e.preventDefault();
  const value = addForm.querySelector("input[type='text']").value;
  
  // create elements
  const li = document.createElement("li");
  const bookName = document.createElement("span");
  const deleteBtn = document.createElement("span");
  
  // add content
  deleteBtn.textContent = "delete";
  bookName.textContent = value;

  // append to document
  li.appendChild(bookName);
  li.appendChild(deleteBtn);
  const list = document.querySelector("#book-list ul");
  list.appendChild(li);
});

.createElement创建元素,.appendChild添加结点,document.createTextNode("some text")创建文本结点

插入结点位置
var parent = document.getElementById("div1");
var p2 = document.getElementById("p2");
var p1 = document.getElementById("p1");
parent.insertBefore(p2, p1);

.insertBefore(p2, p1)在p2前面插入p1元素

替换结点
var parent = document.getElementById("div1");
var p2 = document.getElementById("p2");
var p1 = document.getElementById("p1");
parent.replaceChild(p1, p2);

.replaceChild(new, old)

删除元素
parent.removeChild(p1);

.removeChild(ele)删除元素ele

改变结点样式

.style获取元素所有样式

var li = document.querySelector("li:last-child");
li.style.color = "red";
li.style.marginTop = "60px";

.style.attr=用于修改结点样式,属性名用驼峰命名法
其他写法:
.style.cssText="color:blue;"
.setAttribute("style", "color:blue;")

var li = document.querySelector("li:last-child");
li.className = "test1";

.className= 用于修改类名,会覆盖之前类型

const addForm = document.forms["add-book"];
addForm.addEventListener("submit", function (e) {
  e.preventDefault();
  const value = addForm.querySelector("input[type='text']").value;

  // create elements
  const li = document.createElement("li");
  const bookName = document.createElement("span");
  const deleteBtn = document.createElement("span");

  // add content
  deleteBtn.textContent = "delete";
  bookName.textContent = value;

  // add classes
  bookName.classList.add("name");
  deleteBtn.classList.add("delete");

  // append to document
  li.appendChild(bookName);
  li.appendChild(deleteBtn);
  const list = document.querySelector("#book-list ul");
  list.appendChild(li);
});

.classList.add() 在原来基础上添加新的类名,删除用.classList.remove()

属性
var book = document.querySelector("li:first-child .name");
console.log(book.getAttribute("class"));

.getAttribute()得到结点属性值

var book = document.querySelector("li:first-child .name");
book.setAttribute("class", "name-2");
console.log(book.getAttribute("class"));

.setAttribute(attr, value)设置结点属性

var book = document.querySelector("li:first-child .name");
console.log(book.hasAttribute("class"));

.hasAttribute()判断元素是否具有某种属性

var book = document.querySelector("li:first-child .name");
book.removeAttribute("class");

.removeAttribute(attr)删除属性

checkbox
const hideBox = document.querySelector("#hide");
hideBox.addEventListener("change", function (e) {
  if (hideBox.checked) {
    list.style.display = "none";
  } else {
    list.style.display = "initial";
  }
});

.checked()判断多选框是否选中

自定义属性的值

使用 data-* 属性来嵌入自定义数据
<element data-属性名="somevalue">

<ul class="tabs">
    <li data-target="#about" class="active">About</li>
    <li data-target="#contact">Contact</li>
</ul>
const tabs = document.querySelector(".tabs");
const panels = document.querySelectorAll(".panel");
tabs.addEventListener("click", (e) => {
  if (e.target.tagName == "LI") {
    const targetPanel = document.querySelector(e.target.dataset.target);
    console.log(e.target.dataset);

    Array.from(panels).forEach((panel) => {
      if (panel == targetPanel) {
        panel.classList.add("active");
      } else {
        panel.classList.remove("active");
      }
    });
  }
});

e.target.dataset获取被点击元素的所有自定义数据,e.target.dataset.target获取属性名为data-target的值

注意:以上例子也说明,不要给子项每一个设置监听器,而应该把监听任务交给父元素

DOM加载完成后执行JS

js在dom加载完成之前执行会引发许多问题,如不能获取dom元素,解决方法之一是在body最后引入js文件,另一种方法如下

document.addEventListener('DOMContentLoaded', function(){
// Write your code here ...
}
JS动画
<button onclick="myMove()">Click Me</button> 
function myMove() {
  var elem = document.getElementById("myAnimation");   
  var pos = 0;
  var id = setInterval(frame, 10);
  function frame() {
    if (pos == 350) {
      clearInterval(id);
    } else {
      pos++; 
      elem.style.top = pos + 'px'; 
      elem.style.left = pos + 'px'; 
    }
  }
}

setInterval(frame, 10),每10秒执行一次frame,该函数返回id,clearInterval(id)用于清除定时器

JS帧动画
var item = document.getElementById('item');
item.animate([
    { transform: 'scale(1)', background: 'red', opacity: 1, offset: 0 },
    { transform: 'scale(.5) rotate(270deg)', background: 'blue', opacity: .5, offset: .2 },
    { transform: 'scale(1) rotate(0deg)', background: 'red', opacity: 1, offset: 1 },
  ], {
    duration: 2000, //milliseconds
    easing: 'ease-in-out', //'linear', a bezier curve, etc.
    delay: 10, //milliseconds
    iterations: Infinity, //or a number
    direction: 'alternate', //'normal', 'reverse', etc.
    fill: 'forwards' //'backwards', 'both', 'none', 'auto'
  });

.animate(frame,params)第一个参数是一个数组,里面是每一帧形态,offset表示该帧持续时间占总时间比例

JS继续上一次动画

window.requestAnimationFrame()告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

var start = null;
var element = document.getElementById('SomeElementYouWantToAnimate');
element.style.position = 'absolute';

function step(timestamp) {
  if (!start) start = timestamp;
  var progress = timestamp - start;
  element.style.left = Math.min(progress / 10, 200) + 'px';
  if (progress < 2000) {
    window.requestAnimationFrame(step);
  }
}

window.requestAnimationFrame(step);
Window
window.addEventListener('resize', update);
var x = window.document.getElementById("demo");
update();

function update() {
  x.innerHTML = "Browser inner window width: " + 
    window.innerWidth + ", height: " + window.innerHeight + ".";
}

resize'在窗口缩放时触发,window.innerWidth是浏览器的宽度

网页跳转
window.open("www.jianshu.com");
alert、confirm、prompt
alert("I am an alert box!");

if (window.confirm("Press a button!")) {
    console.log("You pressed OK!");
} else {
    console.log("You pressed Cancel!");
}
var person = prompt("Please enter your name", "123");
Cookies、Local Storage、Session Storage
localStorage.setItem('lunch', 'cereal');
console.log(localStorage.getItem('breakfast'));

localStorage.removeItem('lunch');
localStorage.clear();

sessionStorage.setItem("run", "done");
sessionStorage.getItem("run");
sessionStorage.removeItem("run");
sessionStorage.clear();

document.cookie = "hello=true";
document.cookie = "doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT";
document.cookie = "person=beau; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/"

document.cookie = "person=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";

console.log(document.cookie)
history
alert(window.history.length)

历史记录的长度

history.back()

后退一条历史记录

history.go(-1)

前进或后退历史记录

history.replaceState('code', null, 'http://codepen.io/');
console.log(history.state)

改变浏览器地址,不会加载页面,但会替换当前这条历史

history.pushState(null, null, "codepen");

改变浏览器地址,不会加载页面,但会写入浏览历史

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容

  • 概述 DOM(Document Object Model)文档对象模型,指的是 XML 文档,需要说明的是,HTM...
    bowen_wu阅读 385评论 0 0
  • JavaScript DOM 基础 DOM 即文档对象模型,是操作 HTML/XML 文档的一套方法。通过 DOM...
    Java__JJ阅读 175评论 0 0
  • 节点 节点类型 每个节点都有一个 nodeType 属性,用于表示节点类型。nodeType 属性返回节点的类型。...
    练晓习阅读 448评论 0 4
  • DOM—— Document Object Model 文档对象模型 Documenta对象 —— XML文档(...
    Qingelin阅读 88评论 0 0
  • 目录 1. 基本概念 1.1 Node类型 DOM1级定义了一个Node接口,该接口由DOM中所有节点类型实现。这...
    王童孟阅读 427评论 0 1