2020前端面试(十一)- DOM和BOM相关

点这里,欢迎关注

一. DOM

1.DOM是什么:

DOM(Document Object Model)文档对象模型,是处理可扩展标志语言的标准编程接口。

DOM 是 W3C 的标准; [所有浏览器公共遵守的标准]

2.DOM0级和DOM2级有什么区别:

DOM0级中为某个dom元素绑定多个事件时,只有最后一个事件有效。onclick

DOM2级中可以为单个元素绑定多个事件,每个事件都可以被触发。addEventListener

3.textContent、innerText、innnerHTML、value的区别:

  • textContent用来获取和设置文本内容,与innerText的差别是:textContent获取到的内容包括了元素中的style标签和script标签的内容。
  • innerText只能获取和设置文本内容,不能获取和设置html代码
  • innerHTML可以获取和设置html代码
  • value获取的是表单元素的值

4.关于dom的api有什么:

https://www.cnblogs.com/betgar/articles/5084855.html

节点创建型api:

  • document.createElement()
  • document,createTextNode()
  • parent.cloneNode(true)
  • document.createDocumentFragment() 创建文档片段,解决大量添加节点造成的回流问题

页面修改型API:

  • parent.appendChild(child)
  • parent.insertBefore(newNode,referenceNode) 将新元素添加到父元素中指定的子元素前面
  • parent.removeChild(child)
  • parent.replcaeChild(newChild,oldChild)

节点查询型API:

  • document.getElementById()
  • document.getElementsByTagName() 返回的是一个即时的HTMLCollection类型
  • document.getElementsByName() 根据指定的name属性获取元素,返回的是一个即时的NodeList
  • document.getElementsByClassName() 返回的是一个即时的HTMLCollection
  • document.querySelector() 获取匹配到的第一个元素,采用的是深度优先搜索。
  • docuemnt.querySelectorAll()
    • 返回的是一个非即时的NodeList,也就是说结果不会随着文档树的变化而变化

节点关系型api:

  • 父关系型:
    • node.parentNode()
  • 兄弟关系型
    • node.previouSibling() 返回节点的前一个节点(包括元素节点,文本节点,注释节点)
    • node.previousElementSibling() 返回前一个元素节点
    • node.nextSibling() 返回下一个节点
    • node.nextElementSibling() 返回下一个元素节点
  • 子关系型
    • parent.childNodes() 返回一个即时的NodeList,包括了文本节点和注释节点
    • parent.children() 一个即时的HTMLCollection,子节点都是Element
    • parent.firsrtNode()
    • parent.lastNode()
    • hasChildNodes()

元素属性型api:

  • element.setAttribute(“name”,“value”) 为元素添加属性
  • element.getAtrribute(“name”) 获取元素的属性

元素样式型api:

  • window.getComputedStyle(element) 返回一个CSSStyleDeclaration,可以从中访问元素的任意样式属性。
  • element.getBoundingClientRect() 返回一个DOMRect对象,里面**⭐️ 包括了元素相对于可视区的位置top,left**,以及元素的大小,单位为纯数字。可用于判断某元素是否出现在了可视区域。

5.什么是事件监听:

addEventListener()方法,用于向指定元素添加事件句柄,它可以更简单的控制事件,语法为

element.addEventListener(event, function, useCapture);

第一个参数是事件的类型(如 “click” 或 “mousedown”).

第二个参数是事件触发后调用的函数。

第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的。

事件传递有两种方式,冒泡和捕获

事件传递定义了元素事件触发的顺序,如果你将P元素插入到div元素中,用户点击P元素,

在冒泡中,内部元素先被触发,然后再触发外部元素,

捕获中,外部元素先被触发,在触发内部元素,

6.说说前端中的事件流

什么叫Dom事件流?

事件发生时会在元素节点之间按照特定的顺序传播,整个过程分为捕获阶段,目标阶段和冒泡阶段,这个传播过程叫做Dom事件流。

事件冒泡:从事件源逐级向上传播到DOM最顶层节点的过程。

事件捕获:从DOM最顶层节点逐级向下传播到事件源的过程。

addEventListener用于指定事件处理程序,共接收三个参数。分别是触发事件,事件处理程序函数以及一个布尔值。第三个参数默认为false,表示在该事件的处理函数会在冒泡阶段被调用。若改为true,则表示事件处理函数会在捕获阶段被调用。

IE只支持事件冒泡。

7.如何让事件先冒泡后捕获

原本的事件流中,是先捕获再冒泡。

对于目标元素来说,如果DOM节点通过addEventListener同时绑定了两个事件监听函数,一个用于捕获,一个用于冒泡,那么两个事件的执行顺序是按照代码添加的顺序执行的。所以,先绑定冒泡的函数,再绑定捕获的函数,即可实现。

对于非目标元素来说,可以给捕获事件的处理程序添加一个定时器,将处理程序推入下一个宏任务执行。

8.说一下事件代理:

事件委托是指 不在子节点单独设置事件监听器,而将事件监听器设置在父节点上,再利用冒泡原理使每一个子节点都能触发该事件。

事件委托的优点:只操作一次Dom,提高了程序的性能。

常用于

​ ul和li标签的事件监听,一般采用事件委托机制将事件监听器绑定在ul上。

​ 还适合动态元素的绑定,新添加的子元素不需单独添加事件处理程序。

(1)了解事件代理吗,这样做有什么好处

事件代理/事件委托:利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的事件,

简而言之:事件代理就是说我们将事件添加到本来要添加的事件的父节点,将事件委托给父节点来触发处理函数,这通常会使用在大量的同级元素需要添加同一类事件的时候,比如一个动态的非常多的列表,需要为每个列表项都添加点击事件,这时就可以使用事件代理,通过判断e.target.nodeName来判断发生的具体元素,这样做的好处是减少事件绑定,同事动态的DOM结构任然可以监听,事件代理发生在冒泡阶段

(2)事件委托以及冒泡原理:

事件委托是利用冒泡阶段的运行机制来实现的,就是把一个元素响应事件的函数委托到另一个元素,一般是把一组元素的事件委托到他的父元素上。

委托的优点是减少内存消耗,节约效率

动态绑定事件

事件冒泡,就是元素自身的事件被触发后,如果父元素有相同的事件,如onclick事件,那么元素本身的触发状态就会传递,也就是冒到父元素,父元素的相同事件也会一级一级根据嵌套关系向外触发,直到document/window,冒泡过程结束。

(3)事件代理在捕获阶段的实际应用:

可以在父元素层面阻止事件向子元素传播,也可代替子元素执行某些操作。

9.事件类型相关:

(1)mouseover和mouseenter的区别

mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移出事件是mouseout

mouseenter:鼠标移入子元素时不会再次触发mouseenter事件,对应的移出事件是mouseleave

(2)三种键盘事件的区别:

keyup: 松开键盘触发

keydown:按下键盘触发

keypress:不能识别功能键,比如ctrl,alt,shift,左右箭头。可以区分大小写。

在输入框中按下一个键的全过程:触发keydown/keypress事件->文字键入输入框中->触发keyup事件

按下按键后自动对焦输入框,应该使用keyup,不应该使用keydown/keypress,因为后者会使按键落入输入框中,对于回车键的话还不能使用keypress。

10.静态绑定事件与动态绑定事件的区别:

静态绑定事件是指直接在Html标签上通过οnclick="hide()"来绑定事件。

缺点:

  • html和js文件存在耦合,不符合结构和行为分离的原则。
  • 可能存在引发错误,如果js代码还没加载就触发该事件则会抛出错误

动态绑定事件是指通过js动态绑定事件,element.onclick() element.addEventListener()。

11.元素的位置和大小-三大系列

① offset系列:

⭐️ offsetTop(获取元素位置): 相对于带有‘定位’的父元素的偏移量

offsetHeight: content+padding+border

offsetParent: 返回带有定位的父元素

② cilent系列:

clientTop: 上边框border-top的宽度

⭐️ clientHeight(获取元素宽高): content+padding,不包含border。

③ scroll系列:

⭐️ scrollTop(获取滚动的距离): 向下滚动后,上面被卷去的距离,即隐藏的高度。

scrollHeight: content+padding ,其中的content包含了因为滚动被隐藏的部分。

④ document.clientWidth与document.style.width的区别:

区别1:前者可以获取任意样式表中的width样式值,包括行内样式的,内嵌样式的,外部样式的;后者只能获取行内的样式。

区别2:clientWidth获取的是数字型的,style获取的带有px后缀

区别3:clientWidth包含了padding,而style.width只包含content。

区别4:clientWidth是只读属性,所以一般用于获取元素的大小;而style.width是可读可写的,可用于获取,也可用于修改。

⑤ 特殊:

**获取html元素:**document.documentElement

**获取body元素:**document.body

获取可视区域的宽高:

  • window.innerWeight 获取的宽度包括纵向滚动条的宽度。
  • ⭐️ document.documentElement.clientWidth 获取的是正宗的可视区域的宽度
  • **document.body.clientWidth ** 获取的是body的宽度,即content+padding。

获取window向下滚动时被卷去的高度: window.pageYOffset (注意:不能使用window.scrollTop)

⑥ 判断一个元素是否已经出现在了可视区域:(此问题可应用在懒加载中)

方法一:计算比较麻烦

需满足条件: xxx.offsetTop(需要递归叠加获取)<= window.pageYOffset+document.documentElement.clientHeight

即该元素距页面顶端的距离 <= window向下滚动隐藏的距离+window的可视区域的高度。

方法二:使用element.getBoundingClientRect().top获取在可视区的位置。

window.addEventListener("scroll", function () {
    let viewPortHeight = window.pageYOffset;
    let offset = box2.getBoundingClientRect().top;
    if (offset < viewPortHeight) {
        if (offset + box2.offsetHeight < 0) {
            console.log("他走了");
        } else {
            console.log("他来啦他来啦");
        }
    }
});

12.鼠标坐标

clientX,clientY: 鼠标在可视区的坐标,可视区即展示在用户面前的页面区域

pageX,pageY: 鼠标在整个html页面的坐标。一般实际应用使用pageX和pageY

screenX,screenY: 鼠标在电脑屏幕的坐标,即整个电脑屏幕,15寸这个s

13.js拖动及拖拽功能的实现

(1)拖动功能的实现:

前置条件:

1.拖动事件的三个过程:鼠标按下mousedown,鼠标移动mousemove,鼠标松开mouseup

鼠标按下后执行mousemove事件。

2.盒子采用绝对定位,通过left和top属性来修改位置。

方法一:(直接根据鼠标移动的距离确定元素移动的距离)

鼠标的坐标通过clientX,clientY获取:

盒子的定位信息:鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的offetLeft.

方法二:

鼠标的坐标通过pageX,pageY获取:

先计算鼠标在盒子中的坐标,这是不变的。然后在mousemove的时候通过pageX和pageY减去在盒子中的坐标计算出盒子边缘应该修改为的偏移量。

(2)拖拽功能的实现:

使用html5提供的拖拽API(Drag 和 drop)

拖拽功能涉及的基本事件:

dragstart:在开始拖放元素时触发。(事件源:被拖拽的元素)

  • 这一步需要做的是获取被拖拽元素的id。拖拽事件对象中的dataTransfer属性是专门用来存储拖动过程中的数据的。ev.dataTransfer.setData("key",value)

dragover:在被拖放在某元素内移动时触发。(事件源:目标元素)

  • 阻止dragover的默认事件(不允许被拖拽)

drop:目标元素完全接受被拖放元素时触发。(事件源:目标元素)

  • 阻止drop的默认事件(以链接的形式打开),然后获取之前保存的元素的id ev.dataTransfer.getData("key"),然后将该元素添加到目标元素中。

二. (BOM)浏览器对象模型:

1.实用的BOM属性对象方法:

  • location对象

    • location.href– 返回或设置当前文档的URL
    • location.search – 返回URL中的查询字符串部分。例如 http://www.dreamdu.com/dreamdu.php?id=5&name=dreamdu 返回包括(?)后面的内容?id=5&name=dreamdu
    • location.hash – 返回URL#后面的内容,如果没有#,返回空
    • location. – 返回URL中的域名部分,例如www.dreamdu.com
    • location.hostname – 返回URL中的主域名部分,例如dreamdu.com
    • location.pathname – 返回URL的域名后的部分。例如 http://www.dreamdu.com/xhtml/ 返回/xhtml/
    • location.port – 返回URL中的端口部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回8080
    • location.protocol – 返回URL中的协议部分。例如 http://www.dreamdu.com:8080/xhtml/ 返回(//)前面的内容http:
    • location.assign() – 重定向页面,与location.href一样,会记录历史,能后退页面
    • location.replace() – 设置当前文档的URL,不记录历史,不能后退页面
    • location.reload() – 重载当前页面,相当于F5。添加参数true则表示强制刷新,直接从服务器获取数据,不从浏览器缓存中取数据,相当于Ctrl+F5
  • history对象

    • history.go(n) – 前进或后退指定的页面数;
    • history.back() – 后退一页
    • history.forward() – 前进一页
  • navigator对象

    • navigator包含了用户浏览器的信息
    • navigator.userAgent – 返回用户代理头的字符串表示(就是包括浏览器版本信息等的字符串)
    • navigator.cookieEnabled – 返回浏览器是否支持(启用)cookie

2. setTimeout(fn,100);100毫秒是如何权衡的:

100ms指的是将回调函数加入到任务队列所花的时间。至于具体什么时候执行,需要看主线程的执行栈中是否还有任务在执行。

3.定时器实现动画的最佳时间:16.6ms

大多数电脑显示器的刷新频率是60HZ,大概相当于每秒钟重绘60次。因此,最平滑的动画效的最佳循环间隔是1000ms/60,约等于16.6ms

4.setInterval存在的问题:

定时器的代码执行部分不断的被调入任务队列中,如果定时器的执行时间比间隔时间长,最终可能导致定时器堆叠在一起执行。

js引擎为了解决这个问题,采用的方式是若任务队列中存在这个定期器,则不会将新的定时器放入任务队列,这样做的弊端是可能导致某些间隔被跳过。

解决方法:

循环调用setTimeout来实现setInterval:(即用setTimeout来实现setInterval)

setTimeout(function fn(){
    ...
	setTimeout(fn,delay)
},delay)

5.requestAnimationFrame:

js动画的要求:

一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化。

用定时器实现js动画存在的问题:

定时器回调函数执行的时机不精确。定时器中的延时指的是将回调函数加入到任务队列所需花的时间,如果主线程中还有任务在执行,就不能确保回调函数在放入队列后马上执行,这就造成了执行时机的不精确。

requestAnimationFrame:

特点:requestAnimationFrame采用系统时间间隔,保证了最佳的绘制效率。

使用方法:requestAnimationFrame接收一个回调函数,这个回调函数会在下一次浏览器重绘之前调用。

6.分别用setInterval,setTimeout,requestAnimationFrame制作有个简单的进度条效果:

setInterval:

<div
     style="width: 0; height: 20px; background-color: orange"
     id="div"
     ></div>
<script>
    let timer = setInterval(() => {
        if (parseInt(div.style.width) >= 500) {
            return clearInterval(timer);
        }
        console.log(div.style.width);
        div.style.width = parseInt(div.style.width) + 5 + "px";
        div.innerHTML = parseInt(div.style.width) / 5 + "%";
    }, 16);
</script>

setTimeout:

<div
     style="width: 0; height: 20px; background-color: orange"
     id="div"
     ></div>
<script>
    let timer = setTimeout(function fn() {
        if (parseInt(div.style.width) < 500) {
            div.style.width = parseInt(div.style.width) + 5 + "px";
            div.innerHTML = parseInt(div.style.width) / 5 + "%";
            timer = setTimeout(fn, 16);
        } else {
            clearTimeout(timer);
        }
    }, 16);
</script>

requestAnimationFrame:类似于setTimeout,需要一次次的调用

<div
     style="width: 0; height: 20px; background-color: orange"
     id="div"
     ></div>
<script>
    let timer = requestAnimationFrame(function fn() {
        if (parseInt(div.style.width) < 500) {
            div.style.width = parseInt(div.style.width) + 5 + "px";
            div.innerHTML = parseInt(div.style.width) / 5 + "%";
            requestAnimationFrame(fn);
        } else {
            cancelAnimationFrame(timer);
        }
    });
</script>

7.js中的轮播实现原理?假如一个页面上有两个轮播,你会怎么实现?

1.让图片存在一个数组中,然后将最后一张图片重复添加在数组的头部,将第一张图片重复添加在数组的最后。

2.然后准备一个只能显示一张图片的盒子,对盒子做溢出隐藏处理。

3.通过定时器增减索引,显示对应的图片,实现轮播功能。

如果有两个轮播,可封装一个轮播组件,将需要轮播的图片作为参数传递。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值