记录一次自定义指令实现回到顶部按钮功能,废话不多说,上代码,结尾有完整代码
实现思路
- 自定义一个
directive,并在main.js文件中引入
import util from './util';
- 在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)
}
}
})
- 监听指定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',
}) : '';
})
}
}
- 操作添加回到顶部按钮
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)
}
}
})