JavaScript无刷新切换页面、无刷新返回、无刷新列表、script模板

发布于 / JavaScript / 2 条评论

在浏览器中,都会有一个history对象,用来保存历史信息。

一些网页,为了优化用户体验,使用了无刷新页面切换、无刷新返回等设计,这个设计主要是通过history.replaceState、history.pushState等方法和onhashchange事件等来实现的。

history.pushState

假设你当前的页面为https://www.xxx.com/,则history.pushState(state, null, url) 的作用是将当前地址栏修改为https://www.xxx.com/url,并将这些信息压入到一个类似栈的容器中,当用户有返回事件时,会将信息取出,url变换成之前的url,即实现了返回页面。  

实质上你可以理解为,你的浏览器就像点击了一个a标签一样,地址栏由https://www.xxx.com/变成了https://www.xxx.com/url,但是实际上浏览器没有真正向服务器请求https://www.xxx.com/url的数据。这时,在

第一个参数是一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。

第二个参数保持null即可,浏览器大多会忽略它。

history.replaceState

history.replaceState与history.pushState,两者都可以修改地址栏的数据,两者最大的区别在于,pushState会将信息压入一个类似栈的容器内,用户返回时可以返回上一层url,而replaceState则是类似替换掉栈顶。两者参数相同。

script标签做模板

script标签一般用于执行JavaScript,而把type设置为text/html后,即<script type="text/html" id="tpl_id">,就不会被当做js来执行了,标签内可以写html模板,浏览器也不会渲染。可以在页面需要更新的地方设置个<div id="container">,当需要调用这个模板时,以jQuery为例,使用$('#container').html($('#tpl_id').html());即可将container的内容设置成这个script标签内的内容

script标签做模板后,模板使用时执行js

这里给出一种情况,模板绑定一个js,在模板生效时,执行这个js。由于模板用的就是script标签,内部不可能再嵌套一个script标签,采用的思路是在模板script标签上添加一个属性:load-script,表示使用模板的时候要执行的脚本,在模板管理类上用eval执行一下即可。

if($(ele).attr('load-script') != undefined){
    //执行模板附带的脚本
    eval($("#" + hash).attr('load-script'));
}

location.hash

在url中,#和#后面的东西就是hash。例如https://www.xxx.com/abc#123,这里location.hash返回的就是#123。当url的hash变化时,会触发onhashchange事件。

无刷新多级列表&无刷新返回

假设通过接口/getList?fid=xxx可以获取一个多级列表的数组,fid表示父级id,如果想做到无刷新多级列表&无刷新返回,其中一个思路是将多层级id数据存放在url的hash中,例如https://www.xxx.com/lists#顶级目录id/二级目录id/三级.....这可以利用history.pushState和window.onhashchange来实现。

对于模板,思路是将模板id放在urlhash中,可以通过a标签直接定向到#tpl_id,也可以用函数的方式来调用,不会产生刷新。

下面给出一种实现方式

window.pageManager = {
    container: null,    //装页面的容器
    defaultPage: null,    //默认页面
    tplPage: [],        //script模板集合
    setDeafultPage: function(page){    //设置默认页面
        this.defaultPage = page;
        return this;
    },
    setContainer: function(div){    //设置页面容器
        this.container = div;
        return this;
    },
    init: function(){                //初始化
        //注册hashchange事件
        $(window).on('hashchange', function (){
            hash = location.hash.replace("#", "");    //获取hash
            idx = hash.lastIndexOf('/') //判断是否在子目录
            if(idx == -1){
                //当前在顶级目录,直接变化页面
                window.pageManager.changePage(hash);
            }else{
                fid = hash.substr(idx + 1);
                //当前在列表子目录下,fid是父级id,通过setList变化列表
                window.pageManager.setList(fid);
            }
        });
        //获取全部模板
        tpls = $('script[type="text/html"]');
        for(var i = 0; i < tpls.length; i++){
            this.tplPage.push(tpls[i].id);
        }
        //初始化默认页
        this.changePage(this.defaultPage);
        return this;
    },
    setPage: function(hash){    
        //window.pageManager.setPage,前进到某个页面,调用的是pushState
        history.pushState(hash, "", "#" + hash);
        $("#" + this.container).html($("#" + hash).html());
        if($("#" + hash).attr('load-script') != undefined){
            //执行模板附带的脚本
            eval($("#" + hash).attr('load-script'));
        }
    },
    changePage: function(hash){
        //window.pageManager.changePage,更改当前页面,调用的是replaceState
        history.replaceState(hash, "", "#" + hash);
        $("#" + this.container).html($("#" + hash).html());
        if($("#" + hash).attr('load-script') != undefined){
            eval($("#" + hash).attr('load-script'));
        }
    },
    setList: function(fid){    //设置列表项方法
        $.ajax({
            url : '/getList.php?fid=' + fid,     
            type : 'get',        
            dataType : 'json',                    
            success: function(data){ 
                if(data.code == 200){
                    data = data.data;
                    html = '';
                    for(var i = 0; i < data.length; i++){
                        //构建列表
                    }
                }
            }
        });
        hash = location.hash.replace("#", "");
        new_hash = hash + '/' + fid
        history.pushState(new_hash, "", "#" + new_hash);
    }
};
//设置容器和默认页
window.pageManager
    .setContainer('tpl_container')
    .setDeafultPage('tpl_default')
    .init();

转载原创文章请注明,转载自: 斐斐のBlog » JavaScript无刷新切换页面、无刷新返回、无刷新列表、script模板
  1. 文

    和pjax是同一个原理吗?

    1. kidultff
      @文 不一样,pjax是向服务器请求页面后只修改变动了的部分实现无刷新,但是本质还是请求了html。这个有点类似网易云音乐web端,把不同页面的模板写到一个html内,切换页面的时候直接在同一个页面读取模板就行,不会向服务器重新请求html