vue原生div做触底加载

时间:2024-05-10 16:02:57

第一种:

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

(1)clientHeight:屏幕的高度;

(2)scrollTop:滚动的高度;

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

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

以下代码实例:

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变量代码:

// 搜索模版弹框
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方法代码:

/**
 * 搜索弹框显示
 */
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代码:

.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=触底加载事件

例:

<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>