【JavaScript】DOM进阶/操作元素/Target/冒泡/Intersection API/部分案例

8 篇文章 0 订阅

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 = '';//填入字符串是无效的
});

添加script的方式对比

在这里插入图片描述
在这里插入图片描述

课程打点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值