s94-web
Version:
常用的web工具方法封装---牧人与鱼
249 lines (239 loc) • 8.61 kB
JavaScript
var s94_web = require('s94-web');
var s94_pjax = (function (global){
let document = global?.document;
if (!document) return console.error('缺少 document 对象!');
var cache_prefix = 's94-pjax_';
/**
* 当pjax出错的时候,刷新页面
*/
function onerror(){ window.location.reload() }
/**
* 绑定的元素的click事件的回调函数
* @param {Event} e event
*/
function onclick(e){
if(this.getAttribute('target') == '_blank') return false;
if(pjax(this.getAttribute('href'), e)){
e.preventDefault(); e.stopPropagation();
}
}
/**
* 锚点跳转
* @param {String} id 跳转的ID
*/
function anchorJump(id){
var dom;
if(id && (dom = document.querySelector(id)) ){
s94_web.screenOffsetPage(s94_web.domOffsetPage(dom));
}else{
s94_web.screenOffsetPage(0);
}
}
/**
* 异步请求跳转的页面内容,并按请求的url缓存(便于在popstate中获取)
* @param {String} url 请求跳转地址
*/
function pjax_ajax(url, callback){
s94_web.ajax({
global: false,
url: url,
success: function(res){
if(!res || typeof(res)!="string") return onerror(); //ajax返回的数据不是String,直接跳出
s94_web.cache(cache_prefix+url, res); //把获取的res按请求的url缓存,便于在popstate中获取
typeof(callback)=='function' && callback(res);
},
error: onerror,
});
}
/**
* 根据数据,渲染容器(pjax.container)里面的内容
*/
function display(){
function appendjs(arr, i){
if(i >= arr.length)return;
if (arr[i].src) {
return s94_web.loadJs({src:arr[i].src, refresh:true, parent:arr[i].parent, type: arr[i].type}, function(){appendjs(arr, i+1)});
} else{
var js = document.createElement('script');
js.text = arr[i].innerHTML;
js.type = arr[i].type;
arr[i].parent.appendChild(js);
appendjs(arr, i+1);
}
}
var outer = document.querySelector(pjax.container), scripts=[], fullHtml=false, content='', title='';
if(!outer) return onerror(); //当前页面不存在容器,直接跳出
//内容解析
pjax.html.replace(/<title[^>]*>([\s\S.]*)<\/title>/i,function(m,p1){
title = p1;return '';
})
if (fullHtml = pjax.html.match(/<html[^>]*>([\s\S.]*)<\/html>/i)) {
var htmlDom = document.createElement('html');
htmlDom.innerHTML = fullHtml[1];
var contentDom = htmlDom.querySelector(pjax.container);
if(!contentDom) return onerror(); //需要渲染的内容里面,没有容器元素,直接跳出
content = contentDom.innerHTML;
}else{
content = pjax.html;
}
//容器内容清理
outer.innerHTML = '';
//内容渲染
document.title = title;
s94_web(content).each(function(row){
outer.appendChild(row);
})
scripts = s94_web(outer).find('script').each(function(){
this.parent = this.parentNode;
}).remove();
appendjs(scripts, 0);
//渲染完成后续工作
anchorJump(window.location.href.replace(/^[^\#]+/,'')); //锚点跳转定位
pjax.on(); //绑定渲染内容部分没有绑定pjax的部分元素
s94_web(window).trigger('pjax:display'); //广播pjax:display事件
}
/**
* 修改state
* @param {Object} data 修改的数据
*/
function replaceState(data){
var state = window.history.state || {};
state = s94_web.merge(state, data);
window.history.replaceState(state, "");
}
/**
* 初始化,在pjax.init中调用,页面刷新之前只运行一次。主要用于绑定popstate事件和定义
*/
function init(){
if(init.inited) return;
init.inited = true;
//绑定popstate事件
window.addEventListener('popstate', function(e){
if(!e.state || !e.state.container){ //可能是a标签默认的锚点跳转
replaceState({container:pjax.container, selector_arr:pjax.selector_arr});
}else{
s94_web(window).trigger('pjax:popstate'); //广播pjax:display事件
var state = e.state;
pjax.container = state.container;
pjax.selector_arr = state.selector_arr;
var url = window.location.href.replace(/\#.*$/,'')
pjax.html = s94_web.cache(cache_prefix+url); //从缓存中获取页面数据
if(pjax.html){
display();
}else{
pjax_ajax(url, function(res){
pjax.html = res;
display();
})
}
}
})
}
/* ---------------------------------- 以上为私有方法 ---------------------------- */
/**
* 主方法,无刷新页面跳转
* @param {String} href 跳转地址
* @param {*} e 事件对象
*/
function pjax(href, e){
e = e || window.event || {};
if(!pjax.container || !document.querySelector(pjax.container)) return false;//缺少pjax容器的,跳转失败
if(!href || /^javascript/.test(href)) return false; //地址为空或者以javascript开头的,跳转失败
var to = s94_web.url(href);
var local = s94_web.url(window.location.href);
if(to.origin != local.origin) return false; //地址和当前location不同源的,跳转失败
if(to.href == local.href) return false; //地址和当前location相同,跳转失败
//插入历史记录(假跳转)
window.history.pushState({container:pjax.container, selector_arr:pjax.selector_arr}, "", to.href);
var to_url = to.href.replace(/\#.*$/,'');
if(local.href.replace(/\#.*$/,'') == to_url) {
//仅仅hash部分不同,不对页面重新渲染,只进行锚点跳转
anchorJump(to.hash);
}else{
//ajax获取跳转地址内容,然后重新渲染容器内容
s94_web(window).trigger('pjax:before', e);
pjax_ajax(to_url, function(res){
s94_web(window).trigger('pjax:success', e); //广播pjax:success事件
pjax.html = res;
display();
})
}
return true;
}
pjax.selector_arr = []; //用于存放pjax.on注册的selector
/**
* pjax初始化,用于指定容器(指定容器的时候,会修改state)。第一次初始化会额外绑定popstate事件
* @param {String} container 容器的选择器字符串
*/
pjax.init = function(container){
if(!container || typeof(container)!="string") throw Error('容器参数错误');
pjax.container = container;
//修改state
replaceState({container:pjax.container});
init(); //初始化操作(在当前页面确定pjax容器后执行)
return pjax;
}
/**
* 绑定指定元素,使他们点击后执行pjax
* @param {String} selector 用于指定元素的选择器字符串
*/
pjax.on = function(selector){
if(!pjax.container) return pjax;
if(typeof(selector)=="undefined"){
for (var i = 0; i < pjax.selector_arr.length; i++) {
s94_web(pjax.selector_arr[i]).each(function(){
if (this.lock_pjax) return;
this.lock_pjax = true;
this.addEventListener('click', onclick, false); //绑定渲染内容部分没有绑定pjax的部分元素
})
}
}else if(selector){
if(pjax.selector_arr.indexOf(selector)==-1) {
pjax.selector_arr.push(selector);
replaceState({selector_arr: pjax.selector_arr}); //修改state中的selector_arr,便于popstate中渲染内容后绑定
}
s94_web(selector).each(function(){
if (this.lock_pjax) return;
this.lock_pjax = true;
this.addEventListener('click', onclick, false);
})
}
return pjax;
}
/**
* 解绑指定或所有元素
* @param {String} selector 用于指定元素的选择器字符串,必须和pjax.on传入的selector相同
*/
pjax.off = function(selector){
if(!pjax.container) return pjax;
if(typeof(selector)!="undefined"){
for (var i = 0; i < pjax.selector_arr.length; i++) {
s94_web(pjax.selector_arr[i]).each(function(){
if (!this.lock_pjax) return;
this.lock_pjax = undefined;
this.removeEventListener('click', onclick);
})
}
pjax.selector_arr = [];
replaceState({selector_arr: pjax.selector_arr});
}else{
var index = pjax.selector_arr.indexOf(selector);
if(index == -1) return pjax;
pjax.selector_arr.splice(index, 1);
replaceState({selector_arr: pjax.selector_arr});
s94_web(selector).each(function(){
if (!this.lock_pjax) return;
this.lock_pjax = undefined;
this.removeEventListener('click', onclick);
})
}
return pjax;
}
return pjax;
})(typeof globalThis !== 'undefined' ? globalThis :
typeof self !== "undefined" ? self :
typeof window !== "undefined" ? window :
typeof global !== "undefined" ? global : {});
if (typeof exports === 'object' && typeof module === 'object'){ //CommonJS
module.exports = s94_pjax;
}