DOM树的组织方法
- 继承使子节点可用父节点的方法。
常用方法
选取元素
- 选取整个文档中的标签
// Selecting elements
console.log(document.documentElement);
console.log(document.head);
console.log(document.body);
- 选取元素:querySelector与querySelectorAll
const header = document.querySelector('.header');
const allSections = document.querySelectorAll('.section');
console.log(allSections);
- 通过ID选取
- 通过TagName或类名获取:注意此时获取到的元素为活动的,即在获取到的元素改变时,这个合集也随之改变
document.getElementById('section--1');
//
const allButtons = document.getElementsByTagName('button');
console.log(allButtons);
//
console.log(document.getElementsByClassName('btn'));
创建与插入元素
创建
-
使用
element.insertAdjacentHTML(position, text);
方法
属性可选:- ‘beforebegin’:元素自身的前面。 - ‘afterbegin’:插入元素内部的第一个子节点之前。 - ‘beforeend’:插入元素内部的最后一个子节点之后。 - ‘afterend’:元素自身的后面。
-
使用
document.createElement('div');
创建一个新元素并且可以通过方法控制它的CSS与文本
const message = document.createElement('div');
//控制类
message.classList.add('cookie-message');
//控制文本
message.innerHTML =
'We use cookied for improved functionality and analytics. <button class="btn btn--close-cookie">Got it!</button>';
//控制样式
message.style.backgroundColor = '#37383d';//设置为内联样式
message.style.width = '120%';
console.log(message.style.color);//由于不是内联样式,无法被获取到
// 可用adds or removes the active class替代
cell.classList.toggle("active");
//获取所有属性 getComputedStyle
console.log(getComputedStyle(message).color);//这样就可以获取到所有样式了
//并且可以操纵这个属性
message.style.height =
Number.parseFloat(getComputedStyle(message).height, 10) + 30 + 'px';
header.prepend(message);//插入这个标签中,作为第一个元素
header.append(message);//在这个标签的最后插入元素
header.append(message.cloneNode(true));//想要插入多个时使用clone,否则多个append只能生效一个
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
header.before(message);//在header之前插入元素
header.after(message);//在header之后插入
删除
- message.remove();
- 使用父元素选择器,然后使用 message.parentElement.removeChild(message);
修改样式
样式表中的变量定义:
:root
这个 CSS 伪类匹配文档树的根元素。对于 HTML 来说,:root 表示 元素,除了优先级更高之外,与 html 选择器相同。在声明全局 CSS 变量时 :root 会很有用。
CSS 变量(- -*)
带有前缀–的属性名,比如- -example,表示的是带有值的自定义属性,其可以通过 var 函数在全文档范围内复用的。
var()
var()函数可以代替元素中任何属性中的值的任何部分。var()函数不能作为属性名、选择器或者其他除了属性值之外的值。(这样做通常会产生无效的语法或者一个没有关联到变量的值。)
- 修改样式变量的值
document.documentElement.style.setProperty('--color-primary', 'orangered');
- 获取标签中的属性值:
html的定义:
<img
src="img/logo.png"
alt="Bankist logo"
class="nav__logo"
id="logo"
designer = "aaa"
data-version-number= "bbb"
/>
在js中操纵属性
const logo = document.querySelector('.nav__logo');
console.log(logo.alt);
console.log(logo.className);
//无法读取自定义属性
console.log(logo.designer);
//但是可以通过方法读取
console.log(logo.getAttribute('designer'));
//修改属性
logo.alt = 'Beautiful minimalist logo';
//设置属性
logo.setAttribute('company', 'Bankist');
//一些区别
console.log(logo.src);//获取绝对路径,实际渲染出的值
console.log(logo.getAttribute('src'));//获取文档中的值,相对路径
//获取data-属性
// Data attributes,使用dataset,将属性名转换为驼峰写法!
console.log(logo.dataset.versionNumber);
//操纵类
// Classes
logo.classList.add('c', 'j');
logo.classList.remove('c', 'j');//传入多值是可以的
logo.classList.toggle('c');
logo.classList.contains('c'); // 区别includes
// Don't use,会全部覆盖
logo.clasName = 'jonas';
其他用法
Target属性
- target属性:target 属性返回触发事件的元素,不一定是事件侦听器的元素。(在冒泡时实用)
- currentTarget:当前事件发生的函数所在的元素,一定是事件监听器的元素,相当于this
页面滑动
获取页面偏移量
- section1.getBoundingClientRect();
获取到元素的位置信息
e.target.getBoundingClientRect()确定点击事件发生的元素的位置
- window.pageXOffset,window.pageYOffset获取窗口关于页面的偏移量
- document.documentElement.clientHeight 获取当前窗口的大小
- 使用 window.scrollTo(s1coords.left + window.pageXOffset, s1coords.top + window.pageYOffset); 设置x,y坐标,进行滚动
注意x,y的偏移量问题,即当前位置+当前滚动偏移量
传入一个对象来定义滑动行为
window.scrollTo({
left: s1coords.left + window.pageXOffset,
top: s1coords.top + window.pageYOffset,
behavior: 'smooth'
})
获取窗口滚动动作:
- 使用 window.addEventListener(‘scroll’,func);
对性能不利,触发太频繁
下见intersection api
事件监听器的其他方法
h1.addEventListener('mouseenter', alertH1);//mouseenter属性
//使用on。。。方法,不常用
h1.onmouseenter = function (e) {
alert('onmouseenter: Great! You are reading the heading :D');
};
//removeEventListener方法
setTimeout(() => h1.removeEventListener('mouseenter', alertH1), 3000);
向事件监听器回调函数传参
使用bind方法将参数放到this上,e依然是默认传入的event对象,this变成了变量
const handleHover = function (e) {
if (e.target.classList.contains('nav__link')) {
const link = e.target;
const siblings = link.closest('.nav').querySelectorAll('.nav__link');
const logo = link.closest('.nav').querySelector('img');
siblings.forEach(el => {
if (el !== link) el.style.opacity = this;
});
logo.style.opacity = this;
}
};
// Passing "argument" into handler
nav.addEventListener('mouseover', handleHover.bind(0.5));
nav.addEventListener('mouseout', handleHover.bind(1));
js的冒泡属性
在文档中事件发生时,有三阶段:
- 捕获阶段:事件点击时就发生捕获,此时点击事件在dom树顶端
- 目标阶段:事件一路向下,搜索到点击目标,在目标处处理事件
- 冒泡阶段:事件开始向上传播,遍历其父元素,直到根部,即document节点,而此时,父元素的相同事件也会被触发
当有父子元素关系时,点击子元素时,父元素也会监听到点击事件,这就是冒泡
阻止冒泡的方法:e.stopPropagation();
addEventListener的第三个属性:
document.querySelector('.nav').addEventListener('click', function (e) { }, true);
添加true之后,事件将不再监听冒泡,而是监听捕获事件,这样首先发生事件的会是父元素,而不是点击的实际元素
事件的冒泡特性:
mouseleave- mouseenter
mouseover-mouseout
事件委托*****
使用冒泡特性为同一父元素下的子元素附上同样的监听器
HTML:
<ul class="nav__links">
<li class="nav__item">
<a class="nav__link" href="#section--1">Features</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--2">Operations</a>
</li>
<li class="nav__item">
<a class="nav__link" href="#section--3">Testimonials</a>
</li>
<li class="nav__item">
<a class="nav__link nav__link--btn btn--show-modal" href="#"
>Open account</a
>
</li>
</ul>
传统用法:使用forEach方法创建多个副本
document.querySelectorAll('.nav__link').forEach(el => {
el.addEventListener('click', function (e) {
e.preventDefault();
const id = this.getAttribute('href');
console.log(id);
document.querySelector(id).scrollIntoView({ behavior: 'smooth' });
})
});
使用冒泡:
当点击某特定元素时,开始冒泡,到父元素时,父元素的事件被调用,此时还可以获取到事件发生的特定元素,这样就可以做出特定反应了
document.querySelector('.nav__links').addEventListener('click', function (e) {
e.preventDefault();
// Matching strategy:匹配策略
if (e.target.classList.contains('nav__link')) {
const id = e.target.getAttribute('href');
document.querySelector(id).scrollIntoView({ behavior: 'smooth' });
}
});
DOM Traversing/查找元素方法
const h1 = document.querySelector('h1')
向下搜索
-h1. querySelector() 向下查找孩子节点
- h1.childNodes 可以获取到该元素下所有子节点(text,comment等全包含)
- h1.children 可以获取该元素所有直接子女标签,返回一个HTMlCollection
- h1.firstElementChild 获取第一个子女标签
- h1.lastElementChild 获取最后一个子女标签
向上搜索
- h1.parentNode
- h1.parentElement
- h1.closest(‘.header’) 接受一个查询字符串,查询距离最近的包含该字符串的父元素。querySelector向下找孩子,而closest向上找。
可用于事件委托
如在一个大元素中有许多小元素,在获取点击对象时,点击到其中包含的小元素时,自动获取到离他最近的大元素,这样就可以确定唯一的点击对象,最终操纵这个对象
const workoutEl = e.target.closest('.workout');
寻找兄弟节点
-
h1.previousSibling 之前的兄弟的全部属性(不常用
-
h1.nextSibling 之后的兄弟的全部属性(不常用
-
h1.previousElementSibling 直接获取到元素本身,常用
-
h1.nextElementSibling 直接获取到元素本身,常用
Intersection API
new IntersectionObserver()
传入一个回调函数和可选选项,达到选项设置的阈值时,执行目标函数
const obsCallback = function (entries, observer) {
//传入两个参数,一个为符合条件的对象条目,threshold有几个,entries数组就有几个,一个为observer本身
entries.forEach(entry => {
console.log(entry);
});
};
const obsOptions = {
root: null,//想要与目标元素相交的根元素,为null时即为整个视窗
threshold: [0, 0.2],//相交到什么程度时执行回调函数
rootMargin: '-90px'//设置相交元素的边界值,相当于视窗被限制到90px下方
};
const observer = new IntersectionObserver(obsCallback, obsOptions);
observer.observe(section1);//开始观察目标元素与对象 的相交程度
返回对象示例:
应用举例:网页加载时向下滚动到一定程度,下一模块才出现
// Reveal sections
const allSections = document.querySelectorAll('.section');
const revealSection = function (entries, observer) {
//只有一个threshold
const [entry] = entries;
//由于这个函数在没有交叉时也会被执行,此时isIntersecting返回false,因此需要这个判断
if (!entry.isIntersecting) return;
//entry对象也有target属性,返回当前对象,使用consle.log查看全部
entry.target.classList.remove('section--hidden');
//不再观察这个元素,因为它已经出现了
observer.unobserve(entry.target);
};
const sectionObserver = new IntersectionObserver(revealSection, {
root: null,
threshold: 0.15,
});
allSections.forEach(function (section) {
//设置观察
sectionObserver.observe(section);
//设置隐藏
section.classList.add('section--hidden');
});
案例2:图片懒加载
html:
原始图片为一个缩略图,很模糊,因此加了一个模糊滤镜
src为原始模糊图
data-src为自定义属性,里面放了清晰图片的路径
<img
src="img/digital-lazy.jpg"
data-src="img/digital.jpg"
alt="Computer"
class="features__img lazy-img"
/>
css:
.lazy-img {
filter: blur(20px);
}
- css选择具有某个属性的标签使用:img[data-src](后加方括号带属性)
// Lazy loading images
//css选择具有某个属性的标签使用:
const imgTargets = document.querySelectorAll('img[data-src]');
const loadImg = function (entries, observer) {
const [entry] = entries;
if (!entry.isIntersecting) return;
// Replace src with data-src
entry.target.src = entry.target.dataset.src;
//为什么不直接操纵对象:因为在网速很慢的情况下,去除了懒加载的模糊滤镜,下方图层新图片还没有被加载出来,因此需要在图片被加载后再进行更新
entry.target.addEventListener('load', function () {
entry.target.classList.remove('lazy-img');
});
//已加载的不需要再观察了
observer.unobserve(entry.target);
};
const imgObserver = new IntersectionObserver(loadImg, {
root: null,
threshold: 0,
//让图片提前进入视域,正值增大视域的范围
rootMargin: '200px',
});
imgTargets.forEach(img => imgObserver.observe(img));
幻灯片式展示 代码
事件类型大全
本质上就是通过css的偏移属性:translateX(0%)(100%) (200%)(300%)
多余的图片被偏移到元素之外,设置overflow即可,转换图片也是操纵translateX。
转换公式:100 * (index - currentSlide)
主要是计算出当前在第几个图片,然后就可以传入函数中,控制偏移量到下一个或上一个
要求:点击按钮可以切换,点击下方黑点也可以切换,按下键盘左右也可以切换
效果:
let curSlide = 0;
const maxSlide = slides.length;
// Functions
const createDots = function () {
slides.forEach(function (_, i) {
dotContainer.insertAdjacentHTML(
'beforeend',
`<button class="dots__dot" data-slide="${i}"></button>`
);
});
};
const activateDot = function (slide) {
document
.querySelectorAll('.dots__dot')
.forEach(dot => dot.classList.remove('dots__dot--active'));
document
.querySelector(`.dots__dot[data-slide="${slide}"]`)
.classList.add('dots__dot--active');
};
const goToSlide = function (slide) {
slides.forEach(
(s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`)
);
};
// Next slide
const nextSlide = function () {
//最后一个自动归位
if (curSlide === maxSlide - 1) {
curSlide = 0;
} else {
curSlide++;
}
goToSlide(curSlide);
activateDot(curSlide);
};
const prevSlide = function () {
if (curSlide === 0) {
curSlide = maxSlide - 1;
} else {
curSlide--;
}
goToSlide(curSlide);
activateDot(curSlide);
};
const init = function () {
goToSlide(0);
createDots();
activateDot(0);
};
init();
// Event handlers
btnRight.addEventListener('click', nextSlide);
btnLeft.addEventListener('click', prevSlide);
document.addEventListener('keydown', function (e) {
if (e.key === 'ArrowLeft') prevSlide();
e.key === 'ArrowRight' && nextSlide();
});
dotContainer.addEventListener('click', function (e) {
if (e.target.classList.contains('dots__dot')) {
const { slide } = e.target.dataset;
goToSlide(slide);
activateDot(slide);
}
});
};
slider();
生命周期事件
由document产生,从访问页面到离开页面的过程
// Lifecycle DOM Events
//在document对象上
document.addEventListener('DOMContentLoaded', function (e) {
console.log('HTML parsed and DOM tree built!', e);
});
//加载完成,在window对象上
window.addEventListener('load', function (e) {
console.log('Page fully loaded', e);
});
//离开网页时发出提醒
window.addEventListener('beforeunload', function (e) {
e.preventDefault();
console.log(e);
e.returnValue = '';//填入字符串是无效的
});