@visdoc/pjax是使用ts编写的一个pjax插件,独立单文件,无任何依赖,支持过渡动画,加载动画。
npm install @visdoc/pjax
- linkSelector
- 类型: string | (container: Element|Document) => Element|NodeList|null
- 描述: 连接元素选择器,支持字符串选择器,也可以是一个回调函数。回调函数可返回要监听事件的元素或null,参数container在初始化时为document对象,更新容器内容时为容器元素Element对象。
- 默认: 无
- containerSelectors
- 类型: string[]
- 描述: 需要替换的元素选择器,支持任意选择器,如:['.content','div.box','#container']
- 默认: 无
- options 可选配置
- options.debug
- 类型: boolean
- 描述: 是否开启调试模式,开启后请求加载页面失败,不会重定向
- 默认: false
- options.fetchOptions
- 类型: RequestInit
- 描述: fetch请求配置
- 默认: {method: 'GET',headers: {Accept: 'text/html'}
- options.behavior
- 类型: 'smooth' | 'auto' | 'instant'
- 描述: 页面滚动行为,默认为平滑滚动。用于滚动到锚点元素。
- 默认: 'smooth'
- options.topAnchors
- 类型: string|undefined
- 描述: 锚点元素,用于滚动到锚点元素id,无需添加#,如:top
- 默认:undefined 滚动到页面顶部
- options.eventDelegation
- 类型: boolean
- 描述: 允许事件委托,默认为true,如果为false,则只绑定当前元素,不处理子元素事件。
- 默认:true
- options.loadFailHandler
- 类型: 'redirect' | 'reload' | 'none'
- 描述: 页面加载失败时的,redirect重定向,reload强制刷新当前页面,none不处理。
- 默认: 'redirect' 重定向到加载失败的url。
- options.runInlineScript
- 类型: boolean
- 描述: 如果设置为true,则被替换的script标签中的内联脚本会被执行。
- 默认: false
- options.lock
- 类型: boolean
- 描述: 如果设置为true时,则加载过程中不会处理的新的click事件,直到加载完成。
- 默认: false
- options.cacheMaxLength
- 类型: number
- 描述: 缓存最大长度,默认为10,超过后,会删除最旧的缓存,0则不缓存。
- 默认: 10
- options.autoReplaceTitle
- 类型: boolean
- 描述: 自动替换页面标题,如果新文档存在title则替换。
- 默认: true
- options.logLevel
- 类型: boolean
- 描述: 日志等级,默认为全部日志,可选值有debug、info、warn、error
- 默认: ['debug','info','warn','error']
- options.debug
import Pjax from '@visdoc/pjax'
// Pjax.isSupported() 判断是否支持window.history.pushState,不支持会使用window.location.href重定向页面
// 实例化
const pjax = new Pjax('a', ['#content'])
// 等待初始化就绪 也是在等待页面dom渲染完成
await pjax.waitReadyState()
// 更简洁的模式
await Pjax.create('a', ['#content'])
// 基本使用
await Pjax.factory.loadPage('/page2.html')
// 传入fetch请求头示范
await Pjax.factory.loadPage('/page2.html', { headers: { 'token': '123' } })
// 注意:构造函数中绑定的链接元素,在内容替换过后会自动重新绑定。
// 通过bindElement方法绑定的元素,不会在内容替换过后重新绑定,如果绑定的元素位于被替换的容器内,事件会失效。
// 某些动态渲染的元素,会导致事件失效,则可以用下面的方法重新绑定。
Pjax.factory.bindElement('a')
// 解绑
Pjax.factory.unBindElement('a')
// 为构造函数传入的选择器重新绑定事件
Pjax.factory.reset()
// 指定父容器,只会重新绑定指定容器下的元素
Pjax.factory.reset(document.querySelector('#container'))
pjax.onProgress = (percentage, end) => {
console.log('当前进度:' + percentage + '%,是否结束:' + end ? '是' : '否')
}
// 如果 newElement为字符串,则是回退页面时恢复页面状态的回调
// 替换内容之前的钩子
pjax.beforeReplace = async (oldElement: Element, newElement: Element | string | null) => {
// 添加 fade-out 类,开始淡出动画
oldElement.classList.add('fade-out');
// 等待动画结束
await new Promise(resolve => setTimeout(resolve, 500));
// 移除 fade-out 类,添加 fade-in 类,开始淡入动画
oldElement.classList.remove('fade-out');
}
// 后置的钩子,此时内容已经替换完成
pjax.afterReplace = async (oldElement: Element, newElement: Element | string | null) => {
oldElement.classList.add('fade-in');
// 等待动画结束
await new Promise(resolve => setTimeout(resolve, 500)); // 0.5秒的动画时间
// 移除 fade-in 类
oldElement.classList.remove('fade-in');
}
// 自定义替换内容
pjax.beforeReplace = async (oldElement: Element, newElement: Element | string | null) => {
// 没有从文档中获取到元素
if (newElement === null) newElement = ''
// 替换元素
oldElement.innerHTML = typeof newElement === 'string' ? newElement : newElement.innerHTML
return Promise.reject() // 返回Promise.reject()内部将不再执行默认的替换操作和afterReplace钩子
}
// 该回调仅在 window.history.pushState() 时触发,回退页面时不会触发
pjax.onSwitchPage = (oldUrl: URL, newUrl: URL) => {
console.log('从' + oldUrl.toString() + '跳转到' + newUrl.toString())
}
// 强制刷新页面,等同于window.location.reload()
Pjax.factory.reload(true)
// 恢复到初始加载时的状态
Pjax.factory.reload(false)
// 保存当前页面状态
Pjax.factory.saveState()
// 获取页面状态
Pjax.factory.getState()
document.getElementById('link')
.addEventListener('click', function(e: Event) {
// 执行自定义的逻辑....
// 把事件交给pjax处理
Pjax.factory.handleClickEvent.call(this, e)
})
// 简洁写法
document.getElementById('link').addEventListener('click', Pjax.factory.handleClickEvent)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>进度条动画示例</title>
<style>
#progress-bar-container {
pointer-events: none; /*防止用户在动画期间与内容交互*/
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 1px;
z-index: 9999; /*确保进度条显示在所有内容之上*/
transform: translateY(-100%);
transition: transform .3s ease-in-out;
#progress-bar {
height: 100%;
width: 0;
transition: width 0.3s ease-in-out; /* 平滑过渡效果 */
background-color: #4caf50; /* 进度条颜色*/
}
}
</style>
</head>
<body>
<div id="progress-bar-container">
<div id="progress-bar"></div>
</div>
<div id="doc_content"></div>
<script type="module">
import Pjax from '@visdoc/pjax'
const pjax = new Pjax('#nav_container', ['#doc_content'], {
debug: true,
strictMatchClickEvent: false
})
// 等待初始化就绪
await pjax.waitReadyState()
// 进度条容器
const progressContainer = document.getElementById('progress-bar-container')
// 进度条元素
const progressBar = document.getElementById('progress-bar')
// 利用进度钩子显示进度条
pjax.onProgress = (progress, end) => {
if (progressBar) {
if (end) {
// 如果是结束,先将进度条宽度设置为100%
progressBar.style.width = '100%'
} else {
progressContainer.style.transform = 'translateY(0)'
// 更新进度条的宽度
progressBar.style.width = `${progress}%`
}
}
}
// 内容替换完成将进度条容器移动偏移出窗口
pjax.afterReplace = async (oldElement) => {
if (oldElement.id === 'doc_content') {
// 延迟500毫秒,关闭动画
await new Promise((resolve) => setTimeout(resolve, 500))
progressContainer.style.transform = 'translateY(-100%)'
}
}
// 模拟调用 onProgress
setTimeout(() => pjax.onProgress(30, false), 1000); // 1秒后进度为30%
setTimeout(() => pjax.onProgress(60, false), 2000); // 2秒后进度为60%
setTimeout(() => pjax.onProgress(100, true), 3000); // 3秒后进度为100%,并结束
</script>
</body>
</html>