四个好用的DOM API

8 篇文章 0 订阅
4 篇文章 0 订阅

1. classList.contains

这个方法可以判断某个元素节点是否有某个 class 类名,返回布尔值。比如:

// div 元素是否有 wrapper 类名
div.classList.contains("wrapper");  

封装:

function hasClassName(el, className){
    return el.classList.contains(className);
}

node.contains

除了 classList.contains 方法之外,还有一个 node.contains 方法,这个方法返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点。语法:

node.contains(otherNode);

写成函数的形式:

function nodeContains(parent, child){
    return parent !== child && parent.contains(child);
}

2. classList.toggle

这个 API 可以切换 class 类名,来回的删除、添加指定的类名。这个 API 可以让我们很方便的实现动画效果。比如下面的例子:

<style>
    .wrapper{
        height: 100px;
        width: 100px;
        border: 1px solid #dddddd;
        transition: background-color 1s;
    }
    .toggle{
        background-color: green;
    }
</style>

<body>
    <div class="wrapper"></div>
    <script>
        const div = document.querySelector(".wrapper");
        div.addEventListener("click", (e) => {
            div.classList.toggle("toggle");
        });
    </script>
    
</body>

效果:
class-toggle
把这个 API 封装成一个函数:

function toggleClassName(el, className){
    el.classList.toggle(className);
}

3. scrollIntoView

这个方法可以让当前的元素滚动到浏览器窗口的可视区域内。a 标签的 href 属性可以是哈希标记(#),当点击 a 标签时就会跳转到当前文档中的内部目标位置。scrollIntoView 与之相似,语法:

element.scrollIntoView(alignToTop?, scrollIntoViewOptions?);

两个参数都是可选的,alignToTop 是一个布尔类型参数,如果为 true,元素的顶端将和其所在滚动区的可视区域的顶端对齐。如果为 false,元素的底端将和其所在滚动区的可视区域的底端对齐。

scrollIntoViewOptions 是一个对象。它包含以下属性,这些属性都是可选的:

  • behavior 定义动画过渡效果, "auto"或 “smooth” 之一。默认为 “auto”;
  • block 定义垂直方向的对齐, “start”, “center”, “end”, 或 "nearest"之一。默认为 “start”。
  • inline 定义水平方向的对齐, “start”, “center”, “end”, 或 "nearest"之一。默认为 “nearest”。

比如渲染一个歌曲列表,这个列表很长,手动滑动时很费力。这时可以在侧边 fixed 一个字母表,当点击某个字母时跳到以该字母开头的歌曲位置。

<style>
    .ul{
        list-style: none;
    }
    .ul .letter{
        height: 160px;
        width: 100vw;
        border-bottom: 1px solid #aaaaaa;
        line-height: 160px;
    }
    .sideBar{
        position: fixed;
        right: 0;
        top: 30px;
    }
</style>
<body>
    <div class="wrapper">
        <ul class="ul">

        </ul>
        <ul class="sideBar"></ul>
    </div>
    <script>
        const ul = document.querySelector(".ul");
        const sideBar = document.querySelector(".sideBar");
        const songs = [         // 假如是后端的数据
            { name: "稻香", code: "d" },
            { name: "喜欢你", code: "x" },
            { name: "山水之间", code: "s" },
            { name: "我的梦", code: "w" },
            { name: "光年之外", code: "g" },
            { name: "忽然之间", code: "h" },
            { name: "芒种", code: "m" },
            { name: "red", code: "r" },
            { name: "红玫瑰", code: "h" }
        ];

        (() => {
            compare(songs, "code");
            render(songs);
            renderAlphabet(sideBar, handleClick);
        })();

        function compare(ary, prop, order = 1){
            ary.sort((a,b) => {
                if(a[prop] > b[prop]){
                    return order;
                }else if(a[prop] < b[prop]){
                    return -order;
                }else return 0;
            });
        }
        function renderAlphabet(wrapper, clickCb){
            for(let i = 0;i < 26;i ++){
                let li = document.createElement("li");
                let code = String.fromCharCode(i + 65);
                li.innerHTML = code;
                li.setAttribute("data-letter", code);
                li.addEventListener("click", (e) => clickCb(e, code));
                wrapper.appendChild(li);
            }
        }
        function handleClick(e, code){
            var el = document.querySelector(`.letter-${code}`);
            el && el.scrollIntoView(true);
        }
        function render(list){
            list.forEach(item => {
                var li = document.createElement("li");
                li.innerHTML = item.name;
                li.classList.add("letter", "letter-" + item.code.toLocaleUpperCase());
                ul.appendChild(li);
            });
        }
    </script>
</body>

效果:
scrollIntoView

4. scrollBy

这个方法是 window 上的,元素上也有这个方法,但兼容性不太好。它用来在窗口中按指定的偏移量滚动文档。两种语法:

window.scrollBy(x,y);
window.scrollBy(options);
  • x 是水平滚动的偏移量,单位:像素;
  • y 是垂直滚动的偏移量,单位:像素;
  • options 是一个包含三个属性的对象:
    • top: 等同于 x;
    • left: 等同于 y;
    • behavior: 表示滚动行为,支持参数:smooth (平滑滚动),instant (瞬间滚动),默认值 auto,效果等同于 instant

scrollBy 中的 x、y是相对值。比如下面的例子,在手机端看小说时,当我们点击自动阅读时,页面就会慢慢滚动,这样我们就不用手动滑动了。

<style>
    .btn-group{
        position: fixed;
        bottom: 6vh;
        right: 1vw;
    }
    .start, .stop{
        padding: 1rem;
        background-color: rgba(0,128,0);
        color: wheat;
        border: 1px solid #eeeeee;
        font-size: 1.2rem;
        border-radius: 10px;
    }
    .stop{
        background-color: #f22;
    }
</style>
<body>
    <div class="wrapper">
        // 许多的文字 ...
    </div>
    <div class="btn-group">
        <button class="start">自动阅读</button>
        <button class="stop">停止</button>
    </div>
    <script>
        const start = document.querySelector(".start");
        var key = false;
        var handle = 0;     // 句柄
        // 添加事件
        start.addEventListener('click', function(){
            if(!key){
                key = true;
                play();
            }
        },false);
        document.querySelector(".stop").addEventListener("click",stop,false);

        function stop(){
            key = false;
            window.cancelAnimationFrame(handle);
        }
        function play(){
            handle = window.requestAnimationFrame(play);
            window.scrollBy(0, 10);
        }
    </script>
</body>

上面代码中,handle 是为了在点击 stop 时停止滚动。key 是为了“上锁”,如果不上锁,多次点击 start 按钮后页面回滚动的越来越快。而使用了 key 变量只有第一次点击 start 按钮后才起作用。点击 stop 按钮后再把锁解开。

得知到达底部

这个应用还有一点问题,当滚动到底部后,如果用户往上滑动,这时 requestAnimationFrame 方法还在运作,我们应当在运动到底部时把它停下来。可以修改 play 中的代码:

function play(){
    handle = window.requestAnimationFrame(play);
    window.scrollBy(0, 10);

    // 返回元素内容高度的度量,包括由于溢出导致的视图中不可见内容。
    var scrollHeight = document.documentElement.scrollHeight;
    // 返回元素的内容垂直滚动的像素数
    var scrollTop = Math.ceil(document.documentElement.scrollTop);
    // 返回元素内部的高度(单位像素),包含内边距,但不包括水平滚动条、边框和外边距。
    var clientHeight = document.documentElement.clientHeight;

    var res = scrollHeight - scrollTop - clientHeight;
    if(Math.abs(res) <= 4){
        alert("到达了底部");
        stop();
    }
}

scrollTop 是个小数,因此 scrollHeight + scrollTop 并不一定等于 clientHeight,因此有误差范围。 效果:
自动阅读
这三个方法可能不同的浏览器返回的结果不同,可能有的浏览器 document.body.scrollTop 总是返回 0,而 document.documentElement.scrollTop 却是有值的。这时可以做兼容处理。

scrollTop 不一定兼容 IE,可以使用下面的方法来获取:

function getScrollTop(){
  return document.documentElement.scrollTop || document.body.scrollTop;
}

document.scrollingElement.scrollTop 也可以获取到 scrollTop 的值。 scrollingElement 在标准模式下是文档的根元素: document.documentElement。
当在怪异模式下, scrollingElement 属性返回 HTML body 元素(若不存在返回 null )。但在 IE 中并没有实现。

scrollHeight 也不一定兼容,需要做兼容处理:

function getScrollHeight(){
  return document.documentElement.scrollHeight || document.body.scrollHeight;
}

最后是 clientHeight

function getWindowHeight(){
  var windowHeight = 0;
  if(document.compatMode == "CSS1Compat"){
    windowHeight = document.documentElement.clientHeight;
  }else{
    windowHeight = document.body.clientHeight;
  }
  return windowHeight;
}

scrollBy 类似的还有 scrollTo 属性,scrollTo 的参数不是相对像素,而是绝对的,即:元素的指定坐标位置。一个应用是它可以实现点击回到文档顶部的功能。

$(el).click(function(){
    window.scrollTo(0,0);
});

翻页效果

scrollBy 可以做翻页的效果。比如我们有四个页面,高度都是 100vh:

body,
html {
    margin: 0;
    padding: 0;
    overflow: hidden;
}
.article {
    height: 100vh;
}
.article-1 {
    background-color: green;
}
.article-2 {
    background-color: red;
}
.article-3 {
    background-color: gold;
}
.article-4 {
    background-color: purple;
}

html 结构:

<div class="wrapper">
    <div class="article article-1"></div>
    <div class="article article-2"></div>
    <div class="article article-3"></div>
    <div class="article article-4"></div>
</div>

然后就可以使用防抖 + 鼠标滚动事件实现了:

function throttle(fn, delay) {
    let valid = true
    return function (...args) {
        if (!valid) {
            return false
        }
        valid = false
        setTimeout(() => {
            fn(...args);
            valid = true;
        }, delay);
    }
}
document.addEventListener("mousewheel", throttle(scroll, 200),false);
function scroll(e){
    var wheelDelta = e.wheelDelta;
    if (wheelDelta < 0) {
        window.scrollBy({
            top: window.innerHeight,
            left: 0,
            behavior: "smooth"
        });
    } else {
        window.scrollBy({
            top: -window.innerHeight,
            left: 0,
            behavior: "smooth"
        });
    }
}

behavior: "smooth" 可以让滚动有平滑效果。
翻页效果
如果是客户端就要使用touch事件。思路:比较 touchsart 以及 touchend 事件中 Y 轴的坐标位置之差来判断是向上运动还是向下运动。需要注意的是,在 css 中应设置这么一个属性,当触控事件发生在元素上时,不进行任何操作,以使用自己提供的拖放和缩放行为。关于这个属性的更多用法可以参考 MDN 文档:touch-action

*{
    touch-action: none;
}

然后是 js 代码:

const slider = {
    pageY: 0,
    pageY_end: 0,
    diff: 0,
    touchStart(e){
        this.pageY = e.changedTouches[0].pageY;
    },
    touchEnd(e){
        this.pageY_end = e.changedTouches[0].pageY;
        this.diff = this.pageY - this.pageY_end;
        var top = 0;

        if (this.diff > 0) {
            // 往下滑动
            top = this.height;
        } else if (this.diff < 0) {
            // 往上滑动
            top = -this.height;
        }
        window.scrollBy({
            top: top,
            left: 0,
            behavior: "smooth"
        });
    },
    init(el, height = window.innerHeight){
        this.height = height;
        var self = this;
        var { touchEnd, touchStart } = this;
        el.addEventListener("touchstart", touchStart.bind(self), false);
        el.addEventListener("touchend", touchEnd.bind(self), false);
    }
}
slider.init(document);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值