Repository: Coffcer/coffce-pjax
Branch: master
Commit: 6b78f347a14f
Files: 10
Total size: 20.4 KB
Directory structure:
gitextract_aaoaea37/
├── .brackets.json
├── .gitattributes
├── README.md
├── demo/
│ ├── 1.html
│ ├── 2.html
│ ├── 3.html
│ └── index.html
├── package.json
├── scripts/
│ └── coffce-pjax.js
└── styles/
└── style.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .brackets.json
================================================
{
"sbruchmann.staticpreview.basepath": "G:/轮子/coffce-pjax/"
}
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
================================================
FILE: README.md
================================================
coffce-pjax
===
coffce-pjax可以将页面所有的跳转替换为AJAX请求,把网站改造成单页面应用。
note: 由于浏览器限制,pjax需要在服务器环境下使用,即不要使用file://xxx.html运行。
###有何用处:
* 可以在页面切换间平滑过渡,增加Loading动画。
* 可以在各个页面间传递数据,不依赖URL。
* 可以选择性的保留状态,如音乐网站,切换页面时不会停止播放歌曲。
* 所有的标签都可以用来跳转,不仅仅是a标签。
* 避免了公共JS的反复执行,如无需在各个页面打开时都判断是否登录过等等。
* 减少了请求体积,节省流量,加快页面响应速度。
* 平滑降级到低版本浏览器上,对SEO也不会有影响。
###兼容性:
* Chrome, Firefox, Safari, Android Browser, IE8+等。
* 在IE8和IE9上使用URL Hash,即地址栏的#号。
* 在更低版本的浏览器和搜索引擎蜘蛛上,保持默认跳转,不受影响。
如何使用
---
####安装:
npm install coffce-pjax
#### 引入
``` javascript
// 使用全局变量
var pjax = window.CoffcePJAX
```
``` javascript
// 使用commonJS或AMD
var pjax = require("coffce-pjax");
```
####简单配置:
``` javascript
pjax.init({
// 替换新页面内容的容器
container: "body",
// 是否在低版本浏览器上使用Hash
hash: true
});
```
####完整配置:
``` javascript
pjax.init({
// 选择器,支持querySelector选择器
selector: "a",
// 要替换内容的容器,可为选择器字符串或DOM对象
container: "body",
// 是否在前进后退时开启本地缓存功能
cache : true,
// 是否对低版本浏览器启用hash方案,不启用此项的低版本浏览器则会按照普通模式跳转
hash: false,
// 是否允许跳转到当前相同URL,相当于刷新
same: true,
// 调试模式,console.log调试信息
debug: false,
// 各个执行阶段的过滤函数,返回false则停止pjax执行
filter: {
// 选择器过滤,如果querySelector无法满足需求,可以在此函数里二次过滤
selector: function(a) {},
// 接收到ajax请求返回的内容时触发
content: function(title, html) {}
},
// 各个阶段的自定义函数,将代替默认函数
custom: {
// 自定义更换页面函数,可以在此实现动画效果等
append: function(html, container) {}
},
// 要监听的事件,相当于pjax.on(...),事件列表看下面
events: {}
});
```
接口
---
```javascript
/**
* 初始化
* @param {Object} options 配置,详情见上面↑
*/
pjax.init(config);
```
```javascript
// 注销插件,一般来说你不需要使用这个方法
pjax.destroy();
```
```javascript
/**
* 使用pjax跳转到指定页面
* @param {String} url
* @param {Object} data 要传到新页面的参数,可以为null或undefined
* @param {Function} callback 请求成功时的回调,可以为null或undefined
*/
pjax.turn(url, data, callback);
```
```javascript
/**
* 监听事件,事件类型见下面↓
* @param {String} type 事件类型
* @param {Function} listener 回调
* @param {String} url 只监听某个url,可以是相对和绝对路径
*/
pjax.on(type, listener);
pjax.on(type, url, listener);
```
```javascript
/**
* 解除监听
* @param {String} type 事件类型
* @param {String} url 只监听某个url,可以是相对和绝对路径
*/
pjax.off(type);
pjax.off(type, url);
```
```javascript
/**
* 触发事件
* @param {String} type 事件类型
* @param {Object} args 参数
*/
pjax.trigger(type, args);
```
事件
---
####监听事件
```javascript
// 通过接口监听
pjax.on(type, url, function);
pjax.on(type, function);
```
```javsctipy
// 通过配置监听
pjax.init({
// ....
events: {
type: function(){}
}
});
```
####事件类型
**init**
在每个页面加载完成后触发,有一个object参数:{ title, html }
**end**
在每个页面离开前触发
**ajaxBegin**
在请求开始时触发。有一个object参数: { url, fnb, data, xhr }, url表示新页面的url,fnb表示是否由浏览器前进后退触发,data表示传到新页面的数据,xhr是请求的XMLHttpRequest()实例
**ajaxSuccess**
在请求成功后触发。参数与begin一样。
**ajaxError**
在请求失败后触发。参数与begin一样。
特性
---
* 优先使用标签上的data-coffce-pjax-href,其次使用href
* 标签上若有data-coffce-pjax属性,将作为data属性传递到新页面
```html
// 将跳转到b.html,并传递字符串data
```
服务端配合
---
* 对于PJAX请求,服务端并不需要返回完整的HTML,只返回变动的Content部分即可。对于普通请求(一般由浏览器地址栏直接打开),则需要返回完整的HTML。
* coffce-pjax在发送请求时,会带上请求头COFFCE-PJAX:true,你可以依此来判断当前请求是PJAX请求还是普通请求。
* 由于没有返回完整的HTML,服务端应该将document.title放在请求头COFFCE-PJAX-TITLE里。
注意:
------
作者很懒,没有认真测试过,接口也可能随时变动,使用需自己小心。
License
-----
MIT
================================================
FILE: demo/1.html
================================================
1.html
================================================
FILE: demo/2.html
================================================
2.html
================================================
FILE: demo/3.html
================================================
3.html
================================================
FILE: demo/index.html
================================================
COFFCE-PJAX-DEMO
================================================
FILE: package.json
================================================
{
"name": "coffce-pjax",
"version": "0.0.4",
"description": "A simple pjax library",
"main": "scripts/coffce-pjax.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/Coffcer/Coffce-PJAX.git"
},
"keywords": [
"pjax",
"coffce"
],
"author": "coffce",
"license": "MIT",
"bugs": {
"url": "https://github.com/Coffcer/Coffce-PJAX/issues"
},
"homepage": "https://github.com/Coffcer/Coffce-PJAX"
}
================================================
FILE: scripts/coffce-pjax.js
================================================
/*jshint eqnull: true, expr: true, sub: true, browser: true, devel: true*/
/*global define, module */
/*!
* Coffce-Pjax
* 将页面所有的跳转替换为ajax请求,把网站改造成单页面应用
* 兼容Chrome, Firefox, Safari, Android Browser, IE8+等
* 在IE8和IE9上使用URL Hash,即地址栏的#号,你也可以选择不启用
* 在更低版本的浏览器和搜索引擎蜘蛛上,保持默认跳转,不受影响
*/
(function (window, undefined) {
"use strict";
// 配置
var config = {
// 选择器,支持querySelector选择器
selector: "a",
// 要替换内容的容器,可为选择器字符串或DOM对象
container: "body",
// 是否在前进后退时开启本地缓存功能
cache: true,
// 是否对低版本浏览器启用hash方案
hash: false,
// 是否允许跳转到当前相同URL,相当于刷新
same: true,
// 调试模式,console.log调试信息
debug: false,
// 各个执行阶段的过滤函数,返回false则停止pjax执行
filter: {
// params: element
// 选择器过滤,如果querySelector无法满足需求,可以在此函数里二次过滤
selector: null,
// params: title, html
// 接收到ajax请求返回的内容时触发
content: null
},
// 各个阶段的自定义函数,将替换默认实现
custom: {
// params: html, container
// 自定义更换页面函数,可以在此实现动画效果等
append: null
},
// 事件监听,合并到CoffcePJAX.on()里
events: null
};
// 使用模式 枚举
var SUPPORT = {
// 不支持
PASS: 0,
// 使用Hash
HASH: 1,
// 使用HTML History API
HTML5: 2
};
// 浏览器支持情况
var suppost = history.pushState ? SUPPORT.HTML5 : ("onhashchange" in window ? SUPPORT.HASH : SUPPORT.PASS);
var util = {
/**
* 合并两个对象,浅拷贝
* @param {Object} obj1
* @param {Object} obj2
*/
extend: function (obj1, obj2) {
if (!obj2) return;
for (var key in obj2) {
if (obj2.hasOwnProperty(key)) {
obj1[key] = obj2[key];
}
}
return obj1;
},
/**
* 输出调试信息,仅在config.debug为true时输出
* @param {String} text
*/
log: function (text) {
config.debug && console.log("coffce-pjax: " + text);
},
/**
* 获取url中的路径, 如:www.google.com/abcd 返回 /abcd
* @param {String} url
*/
getPath: function (url) {
return url.replace(location.protocol + "//" + location.host, "");
},
/**
* 通过相对路径获取完整的url
* @param {String} href
*/
getFullHref: function (href) {
// 利用a标签来获取href,除此之外,a标签还能用来获取许多url相关信息
var a = document.createElement("a");
a.href = href;
return a.href;
},
/**
* 判断dom是否匹配选择器
* @param {Object} element
* @param {String} selector
*/
matchSelector: function (element, selector) {
var match =
document.documentElement.webkitMatchesSelector ||
document.documentElement.mozMatchesSelector ||
document.documentElement.msMatchesSelector ||
// 兼容IE8及以下浏览器
function (selector, element) {
// 这是一个好方法,可惜IE8连indexOf都不支持
// return Array.prototype.indexOf.call(document.querySelectorAll(selector), this) !== -1;
if (element.tagName === selector.toUpperCase()) return true;
var elements = document.querySelectorAll(selector),
length = elements.length;
while (length--) {
if (elements[length] === this) return true;
}
return false;
};
// 重写函数自身,使用闭包keep住match函数,不用每次都判断兼容
util.matchSelector = function (element, selector) {
return match.call(element, selector);
};
return util.matchSelector(element, selector);
}
};
var cache = {
key: function (url) {
return "coffce-pjax[" + url + "]";
},
get: function (url) {
var value = sessionStorage.getItem(cache.key(url));
return value && JSON.parse(value);
},
set: function (url, value) {
// storage有容量上限,超出限额会报错
try {
sessionStorage.setItem(cache.key(url), JSON.stringify(value));
} catch (e) {
util.log("超出本地存储容量上线,本次操作将不使用本地缓存");
}
},
clear: function () {
var i = sessionStorage.length;
while (i--) {
var key = sessionStorage.key(i);
if (key.indexOf("coffce-pjax") > -1) {
sessionStorage.removeItem(key);
}
}
},
};
var event = {
// 在浏览器前进后退时执行
popstate: function () {
core.fnb = true;
core.turn(location.href, null, null);
},
// hash改变时执行,由于过滤了手动改变,所以也只在浏览器前进后退时执行
hashchange: function () {
if (!core.fnb) return;
core.turn(location.href.replace("#/", ""), null, null);
},
click: function (e) {
var element = e.target || e.srcElement;
// 过滤不匹配选择器的元素
if (!util.matchSelector(element, config.selector)) return;
// 调用自定义过滤函数
if (config.filter.selector && !config.filter.selector(element)) return;
// 优先使用data-coffce-pjax-href
var url = element.getAttribute("data-coffce-pjax-href");
url = url ? util.getFullHref(url) : element.href;
// 过滤空值
if (url === undefined || url === "") return;
// 阻止默认跳转,
// 在这上面的return,仍会执行默认跳转,下面的就不会了
e.preventDefault ? e.preventDefault() : (window.event.returnValue = false);
// 阻止相同链接
if (!config.same && url === location.href) return;
// 标签上有这个值的话,将作为data传入新页面
var data = element.getAttribute("data-coffce-pjax");
core.fnb = false;
core.turn(url, data, null);
},
bindEvent: function () {
if (suppost === SUPPORT.HTML5) {
window.addEventListener("popstate", event.popstate);
window.addEventListener("click", event.click);
} else {
window.attachEvent("onhashchange", event.hashchange);
document.documentElement.attachEvent("onclick", event.click);
}
},
unbindEvent: function () {
if (suppost === SUPPORT.HTML5) {
window.removeEventListener("popstate", event.popstate);
window.removeEventListener("click", event.click);
} else {
window.detachEvent("onhashchange", event.hashchange);
document.documentElement.detachEvent("onclick", event.click);
}
}
};
var core = {
// Forward And Back,表示当前操作是否由前进和后退触发
fnb: false,
// 显示新页面
show: function (title, html) {
pjax.trigger("end");
document.title = title;
if (config.custom.append) {
config.custom.append(html, config.container);
} else {
config.container.innerHTML = html;
}
pjax.trigger("init");
},
// 跳转到指定页面
turn: function (url, data, callback) {
var eventData = {
url: url,
fnb: core.fnb,
data: data
};
//pjax.trigger("begin", eventData);
// 如果是由前进后退触发,并且开启了缓存,则试着从缓存中获取数据
if (core.fnb && config.cache) {
var value = cache.get(url);
if (value !== null) {
core.show(value.title, value.html);
/*pjax.trigger("success", eventData);
pjax.trigger("end", eventData);*/
return;
}
}
// 开始发送请求
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.setRequestHeader("COFFCE-PJAX", "true");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
eventData.xhr = xhr;
pjax.trigger("ajaxBegin", eventData);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 姑且认为200-300之间都是成功的请求,304是缓存
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
var title = xhr.getResponseHeader("COFFCE-PJAX-TITLE") || document.title,
html = xhr.responseText;
// 内容过滤器
if (config.filter.content && !config.filter.content(title, html)) {
util.log("filter.content过滤不通过");
} else {
callback && callback(data);
pjax.trigger("ajaxSuccess", eventData);
// 显示新页面
core.show(title, html);
if (!core.fnb) {
// 修改URL
if (suppost === SUPPORT.HTML5) {
history.pushState(null, null, url);
} else {
location.hash = util.getPath(url);
}
// 添加到缓存
if (config.cache) {
cache.set(url, {
title: title,
html: html
});
}
}
}
} else {
pjax.trigger("ajaxError", null, eventData);
util.log("请求失败,错误码:" + xhr.status);
}
core.fnb = true;
}
};
xhr.send();
}
};
var pjax = {
ready: false,
events: {},
/**
* 初始化
* @param {Object} options 配置
*/
init: function (options) {
if (suppost === SUPPORT.PASS) {
util.log("不支持该版本的浏览器");
return;
}
util.extend(config, options);
// 将config.container转换为dom
if (typeof config.container === "string") {
var selectorName = config.container;
config.container = document.querySelector(config.container);
if (config.container === null) {
throw new Error("找不到Element:" + selectorName);
}
}
// 监听配置里的事件
if (config.events) {
for (var key in config.events) {
pjax.on(key, null, config.events[key]);
}
}
// 如果一打开就已经带有hash, 则立刻发请求
// 由于hash不会被传到服务器,此时页面多半是首页,如打开www.google.com/#/abcd,其实是打开了www.google.com
if (suppost === SUPPORT.HASH && location.hash.length > 2) {
// 先删了当前内容,防止用户误会
config.container.innerHTML = "";
pjax.ready = true;
core.fnd = false;
core.turn(location.href.replace("#/", ""), null, function () {
pjax.trigger("init");
});
}
event.bindEvent();
if (!pjax.ready) {
pjax.ready = true;
pjax.trigger("init");
}
},
// 注销插件,一般来说你并不需要使用这个方法
destroy: function () {
pjax.events = null;
event.unbindEvent();
util.clearCache();
},
/**
* 使用pjax跳转到指定页面
* @param {String} url
* @param {Object} data 要传到新页面的参数,可以为null
* @param {Function} callback 请求成功时的回调
*/
turn: function (url, data, callback) {
url = util.getFullHref(url);
core.fnb = false;
core.turn(url, data, callback);
},
/**
* 监听事件
* @param {String} type 事件类型
* @param {String} url 指定监听该事件的页面,null表示所有页面都监听
* @param {Function} listener 回调
*/
on: function (type, url, listener) {
// 只有两个参数,跳过中间的url
if (listener === undefined) {
listener = url;
url = null;
} else if (url) {
url = util.getFullHref(url);
}
pjax.events[type] = pjax.events[type] || [];
pjax.events[type].push({
listener: listener,
url: url
});
},
/**
* 解除监听
* @param {String} type 事件类型
* @param {String} url 解绑该事件的页面,null表示所有页面都解绑
*/
off: function (type, url) {
if (url) {
var list = pjax.events[type];
url = util.getFullHref(url);
for (var i = 0; i < list.length; i++) {
if (list[i].url === url) {
list.splice(i, 1);
i--;
}
}
if (list.length) return;
}
delete pjax.events[type];
},
/**
* 触发事件
* @param {String} type 事件类型
* @param {Object} args 参数
*/
trigger: function (type, args) {
var list = pjax.events[type];
if (list) {
for (var i = 0, length = list.length; i < length; i++) {
list[i].listener.call(pjax, args);
}
}
}
};
if (typeof define === "function" && define.amd) {
define([], function () {
return pjax;
});
} else if (typeof module === "object" && typeof exports === "object") {
module.exports = pjax;
} else {
window.CoffcePJAX = pjax;
}
})(window);
================================================
FILE: styles/style.css
================================================
* {
box-sizing: border-box;
}
html {
height: 100%;
background-color: #EFEFEF;
}
body {
margin: 0;
height: 100%;
overflow: hidden;
}
#container {
position: relative;
height: 50%;
border-bottom: solid 1px #C1C1C1;
}
#container p {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
height: 40px;
width: 100%;
color: #555;
text-align: center;
font: 30px "microsoft yahei";
transition: all .3s;
}
#main {
padding: 40px;
height: 50%;
text-align: center;
border-top: solid 1px #FFF;
}
#main a {
margin: 0 20px;
color: #1075BF;
text-decoration: none;
font-family: "microsoft yahei";
}