您的位置:首页 > Web前端 > JavaScript

JS实现二级列表(包含下拉刷新、上拉加载更多、侧滑操作等)

2018-02-26 10:08 627 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。http://blog.csdn.net/xiaoxiaoluckylucky/article/details/79373835本篇文章讲述的是使用js实现可收缩二级列表,包含以下特性:
1,点击打开/收缩二级列表;2,列表下拉刷新数据;3,列表上拉加载更多数据;4,列表item项可侧滑操作(类似于QQ和微信);5,数据缓存;先上效果图:






进入正题,下面是具体实践:功能实现使用到了Framework7框架,Framework7官网地址为:http://framework7.taobao.org。后面我会上传完整demo(包含Framework7库,可直接使用)。

一,准备工作

1,引入Framework7库;引入Framework7的详细教程我就不写了,可以直接看官网教程:http://framework7.taobao.org/get-started/#.WO8wgVK75bU。也可以直接下载这篇文章的demo,demo里已引入所有文件并做好了所有配置,可供参考。

二,具体实现

依旧简单粗暴贴代码吧。下面是完整代码文件结构:


以上图示文件中,主要代码有:monitor_main.js、accordion.js、monitor_main.html、monitor_main_tpl.html、monitor_main.css,其余均为Framework7库文件或配置文件。下面贴出主要代码:1,页面布局monitor_main.html[html] view plain copy<div class="pages navbar-through">  
    <div data-page="monitor_main" class="page">  
        <div class="page-header" style="position: absolute;z-index:9">  
            <div class="page-header-inner">  
                <div class="left">  
                    <a href="#" class="link">  
                        <img src="images/monitor/menu.png" class="nav_open_panel">  
                    </a>  
                </div>  
                <div class="center">JS二级列表</div>  
                <div class="right">  
                    <a href="#"></a>  
                </div>  
            </div>  
        </div>  
        <div style="width:100%; margin-top:0px; height:100%;" id="monitor_areas_list"  
             class="page-content pull-to-refresh-content infinite-scroll" data-ptr-distance="55" data-distance="1">  
  
            <div class="pull-to-refresh-layer">  
                <div class="preloader cus-preloader"></div>  
                <div class="pull-to-refresh-arrow"></div>  
            </div>  
  
            <div class="list-block cusv1-accordion-list" style="margin: 48px 0">  
                <ul id="areas_ul">  
                </ul>  
            </div>  
        </div>  
    </div>  
</div>  

2,列表使用模板monitor_main_tpl.html
[html] view plain copy<li class="cusv1-accordion-item area_accordion_item" data-area_id={{item_area_id}} data-area_index={{item_area_index}}>  
    <a href="#" class="item-link item-content accordion-item-header" style="display:block">  
        <div class="area_list_item_unit1">  
            <span class="area_list_source">{{item_area_name}}</span>  
            <span class="area_equip_num">{{item_equip_num}}</span>  
            <img src="images/monitor/icon_arrow_to_right.png" class="icon_to_expand_area">  
        </div>  
        <div class="area_list_item_unit2">  
            <img src="images/monitor/monitor_location_icon.png" class="loc_icon"/>  
            <span class="equip_list_location" style="width:80%">{{item_area_location}}</span>  
        </div>  
    </a>  
    <div class="accordion-item-content">  
        <div class="list-block">  
            <ul style="padding-left: 0">  
                {{#each subs}}  
                <li class="swipeout equip_according_list_item"  
                    data-area_id={{area_id}} data-device_id={{device_id}} data-device_name={{device_name}}  
                    data-isConcerned={{isConcerned}}>  
                    <div class="swipeout-content">  
                        <div class="concerns_click_area">  
                            <img class="expanded_equips_item_state" src="{{concerns_state_icon}}"/>  
                        </div>  
                        <div class="expanded_equips_click_area">  
                            <span class="expanded_equip_item_source">{{device_name}}</span>  
                            <img src="images/monitor/icon_arrow_to_right.png" class="icon_to_equip_details">  
                        </div>  
                    </div>  
                    <div class="swipeout-actions-right">  
                        <a href="#" class="swipeout-close cancle_concerns equips_item_swipeout_btn"  
                           style="background-color:{{btn_item_left_bg}}">{{btn_item_left_text}}</a>  
                    </div>  
                </li>  
                {{/each}}  
            </ul>  
        </div>  
    </div>  
</li>  

3,收缩列表工具类accordion.js
[javascript] view plain copy/*=============================================================================== 
 ************   Accordion   ************ 
 ===============================================================================*/  
(function () {  
    var $ = $$, app = myApp;  
  
    app.accordionOpenv1 = function (item) {  
        item = $(item);  
        var list = item.parents('.cusv1-accordion-list').eq(0);  
        var content = item.children('.accordion-item-content');  
        if (content.length === 0) content = item.find('.accordion-item-content');  
        var expandedItem = list.length > 0 && item.parent().children('.accordion-item-expanded');  
        if (expandedItem.length > 0) {  
            app.accordionClose(expandedItem);  
        }  
        content.css('height', content[0].scrollHeight + 'px').transitionEnd(function () {  
            if (item.hasClass('accordion-item-expanded')) {  
                content.transition(0);  
                content.css('height', 'auto');  
                var clientLeft = content[0].clientLeft;  
                content.transition('');  
                item.trigger('opened');  
            }  
            /* else { 
             content.css('height', ''); 
             item.trigger('closed'); 
             } */  
        });  
        item.trigger('open');  
        item.addClass('accordion-item-expanded');  
    };  
    app.accordionClosev1 = function (item) {  
        item = $(item);  
        var content = item.children('.accordion-item-content');  
        if (content.length === 0) content = item.find('.accordion-item-content');  
        item.removeClass('accordion-item-expanded');  
        content.transition(0);  
        content.css('height', content[0].scrollHeight + 'px');  
        // Relayout  
        var clientLeft = content[0].clientLeft;  
        // Close  
        content.transition('');  
        content.css('height', '').transitionEnd(function () {  
            /* if (item.hasClass('accordion-item-expanded')) { 
             content.transition(0); 
             content.css('height', 'auto'); 
             var clientLeft = content[0].clientLeft; 
             content.transition(''); 
             item.trigger('opened'); 
             } 
             else {*/  
            content.css('height', '');  
            item.trigger('closed');  
            // }  
        });  
        item.trigger('close');  
    };  
  
})();  

4,逻辑实现monitor_main.js
[javascript] view plain copy/** 
 * Created on 2016/12/18. 
 */  
  
var $$ = Dom7;  
  
var myApp = new Framework7();  
  
var mainView = myApp.addView('.view-main');  
  
var utils = {  
    post: function (data, sf, ef, p) {  
        var url = utils.postUrl();  
        $$.ajax({  
            url: url,  
            async: true,  
            method: 'POST',  
            contentType: 'text/plain',  
            crossDomain: true,  
            data: data,  
            success: function (e) {  
                if ("function" == typeof sf)  
                    sf(e, p);  
            },  
            error: function (e) {  
                console.log(e);  
                if ("function" == typeof ef)  
                    ef(e, p);  
            }  
        });  
    },  
    get: function (url, sf, ef, p) {  
        $$.ajax({  
            url: url,  
            async: true,  
            method: 'GET',  
            contentType: 'application/x-www-form-urlencoded',  
            crossDomain: true,  
            success: function (e) {  
                if ('function' == typeof sf)  
                    sf(e, p);  
            },  
            error: function (e) {  
                if ('function' == typeof ef)  
                    ef(e, p);  
            }  
        });  
    }  
}  
  
var monitorMain = function () {  
    var page, refreshContent, infiniteScroll, isLoading = false, isAreasLoading = false, data  
        , templateStr, template, distreeData, cacheDatas = {}, isOperated = false, swipeout_item_data = {};  
  
    /* 
     * 获取模板文本 
     * */  
    $$.get("pages/monitor/monitor_main_tpl.html", function (data) {  
        templateStr = data;  
    });  
  
    /* 
     * 加载监控页面 
     * */  
    mainView.router.load(  
        {  
            url: 'pages/monitor/monitor_main.html'  
        }  
    );  
  
    /* 
     * 页面pageBeforeAnimation回调 
     * */  
    $$(document).on('pageBeforeAnimation', function (e) {  
        var page = e.detail.page;  
        if (page.name == "monitor_main") {  
            beforeAnimal(page);  
        }  
    });  
  
    /* 
     * 页面pageBeforeAnimation回调执行动作 
     * */  
    function beforeAnimal(page) {  
        var container = $$(page.container)  
        container.find('.navbar').removeClass('navbar-hidden');  
        container.find('.pull-to-refresh-content').css({'margin-top': '-44px', 'padding-bottom': '44px'});  
        container.find('.pull-to-refresh-layer').css({'margin-top': '0'});  
    }  
  
    /* 
     * 页面pageInit回调 
     * */  
    $$(document).on('pageInit', function (e) {  
        page = e.detail.page;  
        if (page.name == "monitor_main") {  
            init(page);  
        }  
    });  
  
    /* 
     * 页面pageInit回调执行动作 
     * */  
    function init(p) {  
        page = $$(p.container);  
        refreshContent = page.find('.pull-to-refresh-content');  
        infiniteScroll = page.find('.infinite-scroll');  
        template = Template7.compile(templateStr);  
        //查询数据  
        beginQuery();  
        //绑定事件  
        bindEvent("on");  
        //构建文本  
        buildAreasBox(distreeData);  
        // 缓存数据-保存数据到sessionStorage,缓存模块相关操作逻辑代码较多,此demo中已去除  
        // sessionStorage.setItem("distreeData", JSON.stringify(distreeData));  
    }  
  
    /* 
     * 查询数据 
     * 先从缓存获取数据,缓存数据为空时再网络请求/模拟数据 
     * 缓存模块相关操作逻辑代码较多,因缓存相关非此demo的重点,此demo中已去除 
     * */  
    function beginQuery() {  
        // distreeData = JSON.parse(sessionStorage.getItem("distreeData"));  
        if (distreeData == null) {  
            // getMonitorAreasInfo("refresh");  
            //无网络环境请求数据,此处自行组装模拟数据  
            assembleAreasData("refresh");  
        } else {  
            buildAreasBox(distreeData);  
        }  
    }  
  
    /* 
     * 绑定/卸载事件 
     * */  
    function bindEvent(t) {  
        var method = t == "on" ? t : "off";  
        if (refreshContent) {  
            //列表下拉刷新事件  
            refreshContent[method]('refresh', refleshHandler);  
        }  
        if (infiniteScroll) {  
            //列表上拉加载更多事件  
            infiniteScroll[method]('infinite', loadMoreHandler);  
        }  
        if (page) {  
            //二级列表打开/收缩事件  
            page[method]('click', ".accordion-item-header", accordionToggle);  
        }  
    }  
  
    /* 
     * 构建列表文本 
     * */  
    function buildAreasBox(items) {  
        data = items;  
        var i, item, htmlstr = '', areaUl = page.find("#areas_ul");  
        for (i = 0; i < items.length; i++) {  
            item = items[i];  
            htmlstr += template(item).trim();  
        }  
        //给列表插入文本  
        areaUl.html(htmlstr);  
    }  
  
    /* 
     * 渲染 
     * 非此demo的重点,此demo中已去除相关代码 
     * */  
    function render(items) {  
        cacheDatas = items;  
        for (var key in items) {  
            var el = page.find(".data-" + key);  
            el.text(items[key]);  
        }  
    }  
  
    /* 
     * 查询数据 
     * */  
    function queryData() {  
        if (isLoading) {  
            resetState();  
            return;  
        }  
        isLoading = true;  
        // getMonitorAreasInfo("refresh");  
        //无网络环境请求数据,此处自行组装模拟数据  
        assembleAreasData("refresh");  
    }  
  
    /* 
     * 重置状态 
     * */  
    function resetState() {  
        isLoading = false;  
        myApp.pullToRefreshDone();  
        myApp.hideIndicator();  
    }  
  
    /* 
     * destroy页面 
     * */  
    function destroyPage(p) {  
        console.log("destroy warning main page");  
        cacheDatas = {};  
        bindEvent("off");  
    }  
  
    /* 
     * 模拟一级列表(区域)数据 
     * */  
    function assembleAreasData(type) {  
        distreeData = [];  
        for (var i = 0; i < 12; i++) {  
            var areaId = '0_' + i;  
            var subsData = simulateEquipDatas(areaId);  
            distreeData[i] = {  
                item_area_index: i,  
                item_area_id: areaId,  
                item_area_name: '数控机床区' + i,  
                item_equip_num: subsData.length,  
                item_area_location: '华南地区/深圳分公司/' + i + '号厂房/中区',  
                subs: subsData  
            }  
        }  
    }  
  
    /* 
     * 模拟二级列表(设备)数据 
     * */  
    function simulateEquipDatas(areaId) {  
        var equipsArr = [];  
        for (var i = 0; i < 6; i++) {  
            equipsArr[i] = {  
                concerns_state_icon: "images/monitor/icon_concerns_no.png",  
                isConcerned: false,  
                btn_item_left_bg: 'darkorange',  
                btn_item_left_text: '点击关注',  
                device_id: '0_1_' + i,  
                device_name: '数控切割机' + i,  
                area_id: areaId  
            }  
        }  
        return equipsArr;  
    }  
  
    /* 
     * 网络请求一级列表(区域)数据 
     * */  
    function getMonitorAreasInfo(type) {  
        var data = JSON.stringify('post_data');  
        var params = {  
            url: 'http://***',  
            data: data  
        }  
        utils.post(params, function (e) {  
            resetState();  
            distreeData = [];  
            // distreeData =  
        }, function (e) {  
            console.log("连接服务器失败,请检查网络");  
            resetState();  
        });  
    }  
  
    /*  
     * 网络请求二级列表(设备)数据  
     * */  
    function getEquipsInfoByAreaId(area_id, _callback) {  
  
        var data = {}  
        data = JSON.stringify(data);  
        var params = {  
            url: 'http://***',  
            data: data  
        }  
        utils.post(params, function (e) {  
            var equipsArr = [];  
            _callback(equipsArr);  
        }, function (e) {  
            console.log("连接服务器失败,请检查网络");  
            resetState();  
        });  
    }  
  
    /*  
     * 二级列表打开/收缩事件  
     * */  
    function accordionToggle(e) {  
        var item = $$(e.target).parents(".cusv1-accordion-item");  
        if (item.length === 0) return;  
        if (item.hasClass('accordion-item-expanded')) {  
            item.find(".icon_to_expand_area").attr("src", "images/monitor/icon_arrow_to_right.png");  
            myApp.accordionClosev1(item);  
        } else {  
            myApp.accordionOpenv1(item);  
            item.find(".icon_to_expand_area").attr("src", "images/monitor/icon_arrow_to_bottom.png");  
        }  
    }  
  
    /* 
     * 刷新事件-列表下拉刷新 
     * */  
    function refleshHandler(e) {  
        console.log("区域列表下拉刷新");  
  
        myApp.showIndicator();  
  
        queryData();  
  
        myApp.hideIndicator();  
  
        // 加载完毕需要重置  
        myApp.pullToRefreshDone();  
    }  
  
    /* 
     * 加载事件-列表上拉加载更多 
     * */  
    function loadMoreHandler(e) {  
        console.log("区域列表上拉加载更多");  
  
        // 如果正在加载,则退出  
        if (isAreasLoading) return;  
  
        // 设置flag  
        isAreasLoading = true;  
  
        //TODO  上拉加载对应操作  
        // getMonitorAreasInfo('loadMore');  
        //无网络环境请求数据,此处自行组装模拟数据  
        // assembleAreasData("loadMore");  
    }  
  
    /*****************************************item侧滑处理-start********************************************/  
  
    /* 
     * item侧滑打开事件触发 
     * 在item侧滑打开事件回调中获取当前item项数据 
     * */  
    $$(document).on('open', '.equip_according_list_item', function () {  
        swipeout_item_data = {};  
        swipeout_item_data[0] = $$(this).attr("data-device_id");  
        swipeout_item_data[1] = $$(this).attr("data-isConcerned");  
        swipeout_item_data[2] = $$(this).attr("data-device_name");  
        swipeout_item_data[3] = $$(this).attr("data-area_id");  
    });  
  
    /* 
     * 侧滑按钮点击事件 
     * 在item侧滑按钮点击事件回调中处理数据变更 
     * */  
    $$(document).on('click', '.equips_item_swipeout_btn', function () {  
        //获取当前item项  
        var item = page.find(".equip_according_list_item[data-device_id ='" + swipeout_item_data[0] + "']");  
        if (swipeout_item_data[1] == 'true') {  
            item.attr('data-isConcerned', false);  
            // TODO 对应数据库操作和缓存操作  
        } else {  
            item.attr('data-isConcerned', true);  
            // TODO 对应数据库操作和缓存操作  
        }  
        isOperated = true;  
    });  
  
    /* 
     * item侧滑关闭事件触发 
     * 在item侧滑关闭事件回调中更新UI 
     * 根据isOperated判断是否有点击侧滑按钮,处理对应逻辑 
     * */  
    $$(document).on('closed', '.equip_according_list_item', function (e) {  
        if (isOperated) {  
            //有点击侧滑按钮  
            if (swipeout_item_data[1] == 'true') {  
                //当前为关注状态时,点击后应更新UI为未关注状态  
                $$(this).find('.expanded_equips_item_state').attr('src', 'images/monitor/icon_concerns_no.png');  
                $$(this).find('.equips_item_swipeout_btn').css('background-color', 'darkorange');  
                $$(this).find('.equips_item_swipeout_btn').text('点击关注');  
                swipeout_item_data[1] = false;  
            } else {  
                //当前为未关注状态时,点击后应更新UI为关注状态  
                $$(this).find('.expanded_equips_item_state').attr('src', 'images/monitor/icon_concerns_yes.png');  
                $$(this).find('.equips_item_swipeout_btn').css('background-color', 'lightgray');  
                $$(this).find('.equips_item_swipeout_btn').text('取消关注');  
                swipeout_item_data[1] = true;  
            }  
            isOperated = false;  
        } else {  
            //未点击侧滑按钮  
  
        }  
    });  
  
    /*****************************************item侧滑处理-end********************************************/  
  
    return {  
        init: init,  
        beforeAnimal: beforeAnimal,  
        resetState: resetState,  
        refleshHandler: refleshHandler,  
        loadMoreHandler: loadMoreHandler,  
        bindEvent: bindEvent,  
        destroyPage: destroyPage  
    }  
}();  

三,写在后面

1,本demo纯手撸,不足之处欢迎批评指正;2,本demo是我从实际项目中剥离出来的,因本篇博客的主题是js二级列表的实现,所以已剔除网络请求、数据库操作、缓存操作、异常处理以及其他相关联部分实现代码;3,在实际项目中,已使用此技术方案实现所有需求,但之后又因需求变更而被废弃;4,后面产品经理要求一级列表和二级列表均能下拉刷新和上拉加载,还提出了一些其他的操作需求,一番蛋疼之后我使用全新技术方案都实现了,后面有空了再整理出来;5,不明之处欢迎交流讨论;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐