2018-04-16 18:35
我将使用history.pushState来解释如何处理Craft CMS的AJAX页面转换的基础知识,首先举例说明如何通过少量修改代码实现的功能:
我现在在几个不同的网站上使用了类似的代码变体,而且效果很好。
与您的模板中扩展的常规布局相比,您需要创建一个AJAX布局。
您预先存在的 _layout.twig 看起来与此类似:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
<head>
{% include '_partials/head' %}
</head>
<body class="preload">
{% include '_partials/header' %}
<main class="js-main" role="main">
{% block content %}
<p>If you see me, you haven’t set your <code>{% verbatim %}{% block content %}…{% endblock %}{% endverbatim %}</code> yet.</p>
{% endblock %}
</main>
{% include '_partials/scripts' %}
</body>
</html>
新的 _ajax-layout.twig 将只是内容块:
{% block content %}
<p>If you see me, you haven’t set your <code>{% verbatim %}{% block content %}…{% endblock %}{% endverbatim %}</code> yet.</p>
{% endblock %}
这是因为我们只想获取每页之间不同的内容。在大多数网站上,标题/导航和页脚保持不变。
通常情况下,您可以在所有模板中扩展布局文件,如下所示:
{% extends '_layout' %}
但是对于AJAX页面转换,您不想获得整个页面,只需要新内容或新的 _ajax-layout.twig 文件。所以我们做这样的事情:
{% extends (craft.request.isAjax and not craft.request.isLivePreview) ? "_ajax-layout" : "_layout" %}
这将检查请求是否是AJAX而不是实时预览。如果我们没有检查实时预览,那么该功能将无法正常工作。您只会在实时预览中看到条目内容,而不是整个页面。
请注意,这需要是三元运算符,因为根据Twig文档,您不能在模板中扩展多个运算符 。
因此,请 使用更新后的AJAX版本替换每个模板中的所有默认 {%extends%}。
我用Velocity.js处理AJAX过渡动画的方式是动画化一个包装每个页面上所有内容的父元素。你可能会找到更适合你的项目的方式,但这对我来说是完美无瑕的。
{# The new extends logic #}
{% extends (craft.request.isAjax and not craft.request.isLivePreview) ? "_layout-ajax" : "_layout" %}
{% block content %}
{{ entry.body }}
<h2>Recent Blogs</h2>
<ul>
{% for entry in craft.entries.section('blog').limit(5).find() %}
<li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
{% endfor %}
</ul>
{% endblock %}
{# The new extends logic #}
{% extends (craft.request.isAjax and not craft.request.isLivePreview) ? "_layout-ajax" : "_layout" %}
{% block content %}
<div class="js-ajax-wrapper">
{{ entry.body }}
<h2>Recent Blogs</h2>
<ul>
{% for entry in craft.entries.section('blog').limit(5).find() %}
<li><a href="{{ entry.url }}">{{ entry.title }}</a></li>
{% endfor %}
</ul>
</div>
{% endblock %}
要在网站上建立链接,请使用 history.pushState:
$(function() {
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
history.pushState({}, '', href);
$main.load(href);
return false;
});
});
这使用 history.pushState 将单击的<a> 标记的href属性添加 到浏览器历史记录中。
它返回false以防止浏览器跟随该链接。
请注意,我委派了点击事件。这是因为如果我们做了像......
$('a').on('click', function() {
var href = $(this).attr("href");
history.pushState({}, '', href);
$main.load(href);
return false;
});
...任何 被点击的新的 <a>标签都会像正常一样运行,并且不会使用 history.pushState。这适用于整个JavaScript中的大部分内容。您需要确保所有内容在添加到DOM后仍然有效。
大多数网站都有一致的页眉和页脚,这些页面和页脚不会发生变化,并且是一个主要的内容区域。所以我们需要使用AJAX来用新内容替换现有的内容。
我选择使用 带有.js-main 类的语义 <main>标记 ,它将包含所有被替换和添加的内容:
$(function() {
var $main = $('.js-main');
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
history.pushState({}, '', href);
$main.load(href);
return false;
});
});
当页面加载时,我们需要能够运行代码,无论是普通页面加载还是AJAX页面加载。所以我们将添加函数来处理这两个:
$(function() {
var $main = $('.js-main'),
/* ----- Do this when a page loads ----- */
init = function() {
/* ----- This is where I would run any page specific functions ----- */
},
/* ----- Do this for ajax page loads ----- */
ajaxLoad = function(html) {
init();
/* ----- Here you could maybe add logic to set the HTML title to the new page title ----- */
},
/* ----- This runs on the first page load with no ajax ----- */
init();
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
history.pushState({}, '', href);
$main.load(href);
return false;
});
});
为确保外部链接正常打开,我们需要检查我们是否 仅在内部链接上使用 history.pushState:
$(function() {
var $main = $('.js-main'),
/* ----- Do this when a page loads ----- */
init = function() {
/* ----- This is where I would run any page specific functions ----- */
},
/* ----- Do this for ajax page loads ----- */
ajaxLoad = function(html) {
init();
/* ----- Here you could maybe add logic to set the HTML title to the new page title ----- */
},
loadPage = function(href) {
$main.load(href + 'main>*', ajaxLoad);
};
/* ----- This runs on the first page load with no ajax ----- */
init();
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
if (href.indexOf(document.domain) > -1 || href.indexOf(':') === -1) {
history.pushState({}, '', href);
loadPage(href);
return false;
}
});
});
现在让我们使浏览器的后退和前进按钮与AJAX转换一起工作。我们将监听 popstate 事件,并确保我们只在事件发生时才采取行动:
$(function() {
var $main = $('.js-main'),
changedPage = false,
/* ----- Do this when a page loads ----- */
init = function() {
/* ----- This is where I would run any page specific functions ----- */
},
/* ----- Do this for ajax page loads ----- */
ajaxLoad = function(html) {
init();
/* ----- Here you could maybe add logic to set the HTML title to the new page title ----- */
/* ----- Used for popState event (back/forward browser buttons) ----- */
changedPage = true;
},
loadPage = function(href) {
$main.load(href + 'main>*', ajaxLoad);
};
/* ----- This runs on the first page load with no ajax ----- */
init();
$(window).on("popstate", function(e) {
// -------------------------------------
// If there was an AJAX page transition already,
// then AJAX page load the requested page from the back or forwards button click.
// Variable initially set after the $main variable.
// -------------------------------------
if (changedPage) loadPage(location.href);
});
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
if (href.indexOf(document.domain) > -1 || href.indexOf(':') === -1) {
history.pushState({}, '', href);
loadPage(href);
return false;
}
});
});
在这一点上,AJAX页面过渡的一个简单的删除和替换。让我们通过添加一些动画来修饰它。我将 在 Velocity UI Pack中使用 Velocity.js,替换loadPage 函数的内容 :
$(function() {
var $main = $('.js-main'),
changedPage = false,
/* ----- Do this when a page loads ----- */
init = function() {
/* ----- This is where I would run any page specific functions ----- */
},
/* ----- Do this for ajax page loads ----- */
ajaxLoad = function(html) {
init();
/* ----- Here you could maybe add logic to set the HTML title to the new page title ----- */
/* ----- Used for popState event (back/forward browser buttons) ----- */
changedPage = true;
},
loadPage = function(href) {
$main.wrapInner('<div class="new-results-div" />');
/* ----- Set height of $main to ensure the footer doesn't jump up ----- */
var newResultsHeight = $('.new-results-div').outerHeight();
$main.height(newResultsHeight);
$('.js-ajax-wrapper').velocity('transition.fadeOut', {
/* ----- Upon completion of animating out content put user at top of page. ----- */
complete: function(){
$('html').velocity("scroll", {
duration: 0,
easing: "ease",
mobileHA: false
});
}
});
$.ajax({
type: 'POST',
url: href,
data: {},
success: function(result){
/* ----- Where the new content is added ----- */
$main.html(result);
/* ----- Wrap content in div so we can get it's height ----- */
$main.wrapInner('<div class="new-results-div" />');
/* ----- Get height of new container inside results container and set $main to it so there's no content jumpage ----- */
var newResultsHeight = $('.new-results-div').outerHeight();
$main.height(newResultsHeight);
/* ----- Bring In New Content ----- */
$('.js-main .js-ajax-wrapper').velocity('transition.fadeIn', {
visibility: 'visible',
complete: function() {
/* ----- Removes the temp height from $main ----- */
$main.css('height', '');
ajaxLoad();
}
});
},
error: function(){
console.log("error.");
location.reload();
}
});
};
/* ----- This runs on the first page load with no ajax ----- */
init();
$(window).on("popstate", function(e) {
// -------------------------------------
// If there was an AJAX page transition already,
// then AJAX page load the requested page from the back or forwards button click.
// Variable initially set after the $main variable.
// -------------------------------------
if (changedPage) loadPage(location.href);
});
$(document).on('click', 'a', function() {
var href = $(this).attr("href");
if (href.indexOf(document.domain) > -1 || href.indexOf(':') === -1) {
history.pushState({}, '', href);
loadPage(href);
return false;
}
});
});
这就是处理带有history.pushState的AJAX页面转换的基本代码。此时,您应该能够通过动画AJAX页面转换浏览您的Craft CMS网站。