vue原生div做触底加载

第一种:

触底加载和图片懒加载的思路一样,屏幕的高度加上滚动的高度快要大于最后一个元素距离顶部的高度的时候就开始加载数据;

(1)clientHeight:屏幕的高度;

(2)scrollTop:滚动的高度;

(3)offsetTop:最后一个元素距离顶部的高度;

if ((clientHeight + scrollTop + 快要到底的高度) >= offsetTop) 获取数据;

以下代码实例:

html代码:

html 复制代码
<el-tooltip content="打开搜索框" placement="bottom" effect="light">
    <i class="el-icon-search" @click.stop="openSearch"></i>
</el-tooltip>

<!-- 搜索弹框 -->
<div class="searchMask" v-if="searchTemplateShow" @click.stop>
    <div class="searchBox" @click.stop>
        <div class="searchHeader">
            <i class="el-icon-close" @click.stop="searchTemplateShow = false"></i>
            <el-input
                v-model="searchInfo.title"
                placeholder="搜索模版"
                clearable
                prefix-icon="el-icon-search"
                @keyup.enter.native="search"
                @keyup.ctrl.enter.native="openNewLink"
                style="width: 100%;"
                class="input-with-select">
                <el-select v-model="templateType" slot="prepend" placeholder="请选择模版类型" @change="search">
                    <el-option v-for="item in templateTypeData" :key="item.value" :label="item.label" :value="item.value"></el-option>
                </el-select>
            </el-input>
        </div>
        <div class="emptySearch" v-if="wtf">
            <img src="@/assets/img/emptySearch.png" />
            <div>搜索无结果</div>
        </div>
        <div class="searchCenter" v-else>
            <div class="searchContent" v-loading="templateLoading" :style="`height:${templateLoading ? '452px' : 'auto'};`">
                <div class="searchContentTitle">选择模版</div>
                <div class="searchContentBox" ref="searchContentBox" @scroll="scrollBottom">
                    <div class="searchItem" v-for="(item, index) in templateData" :key="item.id" @click.stop="createDoc(item.id)" :ref="`searchItem${index}`">
                        <div class="searchItemIcon">
                            <img src="@/assets/img/templateSearchIcon.png" />
                        </div>
                        <div class="searchItemContent">
                            <div class="searchItemTitle one" v-if="item.title">{{ item.title }}</div>
                            <div class="searchItemDesc" v-if="item.desc">{{ item.desc }}</div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="searchFooter">
                <span>{{ total || 0 }}</span>
                <span>结果</span>
                <span>支持ENTER搜索、CTRL+ENTER新窗口打开</span>
            </div>
        </div>
    </div>
</div>

js变量代码:

javascript 复制代码
// 搜索模版弹框
searchTemplateShow: false,
// 模版类型 1:热门 2:我的
templateType: 1,
templateTypeData: [{
    label: "热门",
    value: 1
},{
    label: "我的",
    value: 2
}],
// 搜索条件对象
searchInfo: {
    title: ""
},
// 搜索模版loading
templateLoading: false,
// 搜索模版数据
templateData: [],
page: 1,
total: null,
mask: true,
wtf: false

js方法代码:

javascript 复制代码
/**
 * 搜索弹框显示
 */
openSearch() {
    if (!this.userInfo.type) {
        this.showLogin = true;
        return false;
    };
    Object.keys(this.searchInfo).forEach(key => {
        this.searchInfo[key] = "";
    });
    this.searchTemplateShow = true;
    this.search();
},
/**
 * 搜索
 */
search() {
    this.templateLoading = true;
    this.page = 1;
    this.templateData = [];
    this.mask = true;
				this.wtf = false;
    this.getTemplateData();
},
/**
 * 滚动到下面
 */
scrollBottom() {
    let clientHeight = this.$refs.searchContentBox.clientHeight;
    let scrollTop = this.$refs.searchContentBox.scrollTop;
    let offsetTop = this.$refs[`searchItem${this.templateData.length - 1}`][0].offsetTop;
    if (((clientHeight + scrollTop + 300) >= offsetTop) && this.mask) this.getTemplateData();
},
/**
 * 获取模版数据
 */
getTemplateData: debounce(async function () {
    this.templateLoading = true;
    let params = {
        ...this.searchInfo,
        page: this.page,
        size: 10,
        is_not_template: 1
    }
    let res = await document.getTemplateData(params, this.templateType);
    if (res.code == 200) {
        this.templateLoading = false;
		res.data.data.forEach(item => {
			this.templateData.push(item);
		});
        this.total = res.data.total;
		this.wtf = this.templateData.length == 0 ? true : false;
		this.page == res.data.last_page ? this.mask = false : this.page++;
    };
}, 300),
/**
 * 打开新链接搜索
 */
openNewLink() {
    window.open(window.location.href);
},
/**
 * 创建文档
 */
createDoc(id) {
    if (!this.userInfo.type) {
        this.showLogin = true;
        return false;
    };
    this.$parent.createDoc(id);
    this.searchTemplateShow = false;
}

css代码:

css 复制代码
.el-icon-search, .el-icon-plus {
    width: 24px;
    display: flex;
    align-items: center;
    justify-content: center;

    &:hover {
        background-color: rgba(0,0,0,.04);
        border-radius: 2px;
        cursor: pointer;
    }
}

.searchMask {
	width: 100vw;
	height: 100vh;
	background-color: rgba(30, 30, 30, .8);
	position: fixed;
	bottom: 0;
	left: 0;
	z-index: 11;
	display: flex;
	align-items: center;
	flex-direction: column;
	justify-content: center;
	
	.searchBox {
	    width: 600px;
        height: auto;
        background: #FFFFFF;
        box-shadow: 0px 6px 28px -3px rgba(165,165,165,0.36);
        border-radius: 10px;
        
        .searchHeader, .searchContentBox, .emptySearch, .searchContent, .searchFooter {
            width: 100%;
            box-sizing: border-box;
        }
        
        .searchHeader {
            height: 117px;
            padding: 60px 28px 13px 28px;
            border-bottom: 1px solid #F5F5F5;
            position: relative;
            
            .el-icon-close {
                position: absolute;
                top: 24px;
                right: 23px;
                font-size: 18px;
                color: #111111;
                cursor: pointer;
            }
            
            /deep/ .el-select .el-input {
                width: 95px;
            }
            
            /deep/ .input-with-select .el-input-group__prepend {
                background-color: #fff;
            }
        }
        
        .searchCenter {
            height: auto;
            
            .searchContent {
                height: auto;
                
                .searchContentTitle {
                    width: 100%;
                    box-sizing: border-box;
                    padding: 20px 28px 10px 28px;
                    font-weight: 400;
                    font-size: 15px;
                    color: #999999;
                }
                
                .searchContentBox {
                    width: 100%;
                    height: auto;
                    max-height: 400px;
                    overflow-y: auto;
                    
                    .searchItem {
                        width: 100%;
                        height: auto;
                        box-sizing: border-box;
                        padding: 12px 28px;
                        display: flex;
                        cursor: pointer;
        
                        &:hover {
                            background-color: rgba(51, 77, 102, 0.06);
                        }
                        
                        .searchItemIcon {
                            width: 16px;
                            height: 16px;
                            margin: 2px 12px 0 0;
                            
                            img {
                                width: 100%;
                                height: 100%;
                            }
                        }
            
                        .searchItemContent {
                            max-width: 508px;
                            
                            .searchHeader, .searchContent {
                                max-width: 508px;
                                font-weight: 400;
                            }
                            
                            .searchItemTitle {
                                font-size: 15px;
                                color: #333333;
                                line-height: 21px;
                            }
                            
                            .searchItemDesc {
                                font-size: 13px;
                                color: #999999;
                                line-height: 18px;
                            }
                        }
                    }
                }
            }
        
            .searchFooter {
                height: 40px;
                padding: 10px 28px;
                box-shadow: 0 6px 28px -3px rgba(165,165,165,0.36);
                font-weight: 400;
                font-size: 14px;
                color: #999999;
                line-height: 20px;
                
                & > span:first-of-type {
                    color: rgba(0, 0, 0, 0.64);
                    margin-right: 5px;
                }
                
                & > span:last-of-type {
                    color: rgba(0, 0, 0, 0.64);
                    margin-left: 16px;
                }
            }
        }
        
        .emptySearch {
            height: 295px;
            font-size: 14px;
            color: rgba(0, 0, 0, 0.4);
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            
            img {
                width: 128px;
                height: 128px;
                margin-bottom: 20px;
            }
        }
	}
}

第二种:

使用element ui的v-infinite-scroll属性添加触底加载事件:

v-infinite-scroll=触底加载事件

例:

javascript 复制代码
<template>
  <ul class="infinite-list" v-infinite-scroll="load" style="overflow:auto">
    <li v-for="i in count" class="infinite-list-item">{{ i }}</li>
  </ul>
</template>

<script>
  export default {
    data () {
      return {
        count: 0
      }
    },
    methods: {
      load () {
        this.count += 2
      }
    }
  }
</script>
相关推荐
星星会笑滴3 分钟前
vue+node+Express+xlsx+emements-plus实现导入excel,并且将数据保存到数据库
vue.js·excel·express
前端百草阁23 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜24 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish25 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple25 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five26 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54127 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
RAY_CHEN.28 分钟前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
酷酷的威朗普28 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省28 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript