Vue自定义指令实现回到顶部按钮功能

205 阅读2分钟

记录一次自定义指令实现回到顶部按钮功能,废话不多说,上代码,结尾有完整代码

实现思路

  1. 自定义一个directive,并在main.js文件中引入
import util from './util';
  1. 在util.js文件中写入代码,定义为v-back-up
Vue.directive('back-up', {
  /**
   * inserted:被绑定元素插入父节点时调用 
   * el:指令所绑定的元素,可以用来直接操作 DOM
   */
  inserted (el) {
    // 添加滚动监听事件
    el.addEventListener('scroll', handleScroll, true)
  },
  update (el) {
    // 更新回到顶部按钮是否显示状态
    isNone(el);
  },
  unbind (el) {
    // 只调用一次,指令与元素解绑时调用,去除按钮及列表绑定的滚动监听事件
    const backDOM = document.querySelector('#app .backUp');
    if (backDOM) {
      backDOM.remove();
    }
    if (el) {
      el.removeEventListener('scroll', handleScroll, true)
    }
  }
})
  1. 监听指定DOM滚动事件
function handleScroll (e) {
  // 这里指定为900px
  if (e.target.scrollTop >= 900) {
    backUp(e.target, true);
  } else {
    backUp(e.target, false);
  }
}

function backUp (val, isShow = false) {
  const backDOM = document.querySelector('#app .backUp');
  if (isShow) {
    addBackUp(val);
  } else {
    if (backDOM) {
      backDOM.remove();
    }
  }
  if (backDOM) {
    // 监听点击事件
    backDOM.addEventListener("click", () => {
      val ? val.scrollTo({
        top: 0,
        behavior: 'smooth',
      }) : '';
    })
  }
}
  1. 操作添加回到顶部按钮
function addBackUp (val) {
  // 获取DOM元素在页面上的位置,用calc计算出回到顶部按钮的绝对位置
  const backDOM = document.querySelector('#app .backUp');
  const cb = val.getBoundingClientRect().bottom;
  const cr = val.getBoundingClientRect().right;
  if (!backDOM) {
    const dom = document.createElement("div");
    const puterStyle = `
    box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.12);
    opacity: 0;
    `
    dom.setAttribute('style', puterStyle);
    dom.setAttribute('class', 'backUp');
    dom.innerHTML = `
    <img src="../xxx.png" style="width: 100%;height: 100%;border-radius: 50%;object-fit: cover;" />
    `;
    document.querySelector('#app').appendChild(dom);
  } else {
    backDOM.style.opacity = '1';
    backDOM.style.top = `calc(${cb}px - 40px - ${backDOM.offsetWidth}px)`;
    backDOM.style.left = `calc(${cr}px - 40px - ${backDOM.offsetHeight}px)`;
  }
}

完整代码

function addBackUp (val) {
  const backDOM = document.querySelector('#app .backUp');
  const cb = val.getBoundingClientRect().bottom;
  const cr = val.getBoundingClientRect().right;
  if (!backDOM) {
    const dom = document.createElement("div");
    const puterStyle = `
    box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.12);
    opacity: 0;
    `
    dom.setAttribute('style', puterStyle);
    dom.setAttribute('class', 'backUp');
    dom.innerHTML = `
    <img src="../xxx.png" style="width: 100%;height: 100%;border-radius: 50%;object-fit: cover;" />
    `;
    document.querySelector('#app').appendChild(dom);
  } else {
    backDOM.style.opacity = '1';
    backDOM.style.top = `calc(${cb}px - 40px - ${backDOM.offsetWidth}px)`;
    backDOM.style.left = `calc(${cr}px - 40px - ${backDOM.offsetHeight}px)`;
  }
}

function backUp (val, isShow = false) {
  const backDOM = document.querySelector('#app .backUp');
  if (isShow) {
    addBackUp(val);
  } else {
    if (backDOM) {
      backDOM.remove();
    }
  }
  if (backDOM) {
    // 监听点击事件
    backDOM.addEventListener("click", () => {
      val ? val.scrollTo({
        top: 0,
        behavior: 'smooth',
      }) : '';
    })
  }
}

function handleScroll (e) {
  if (e.target.scrollTop >= 900) {
    backUp(e.target, true);
  } else {
    backUp(e.target, false);
  }
}

function isNone (val) {
  // 判断是否为不显示状态
  if (val.style.display !== 'none') {
    if (val.scrollTop >= 900) {
      backUp(val, true);
    } else {
      backUp(val, false);
    }
  }
}

// 使用Vue.directive声明自定义指令back-up
Vue.directive('back-up', {
  /**
   * inserted:被绑定元素插入父节点时调用 
   * el:指令所绑定的元素,可以用来直接操作 DOM
   */
  inserted (el) {
    // 添加滚动监听事件
    el.addEventListener('scroll', handleScroll, true)
  },
  update (el) {
    // 更新回到顶部按钮是否显示状态
    isNone(el);
  },
  unbind (el) {
    // 只调用一次,指令与元素解绑时调用,去除按钮及列表绑定的滚动监听事件
    const backDOM = document.querySelector('#app .backUp');
    if (backDOM) {
      backDOM.remove();
    }
    if (el) {
      el.removeEventListener('scroll', handleScroll, true)
    }
  }
})

如发现更好的方法,欢迎大家留言~