前言
昨天写了篇给Typecho加上pjax的文章,现在再来介绍下ajax评论的部分(虽然也有大佬写过了)
如果你沒有js基础或者不太熟悉jquery请忽略本文
下面的代码适用于我自己写的这个主题,主要是提供一些思路和方法,具体要移植到你自己的模板上还需要具体测试和修改,也算是抛砖引玉吧 :)
一些错误希望各位大佬指正
正文
基本思路
监听评论表单的提交事件,使用ajax向typecho的后端发送评论的数据并取回服务端响应的内容(成功或失败),然后直接呈现在当前页面。
之前我在处理父级评论的时候,涉及到这样一个问题:
如果你在设置中:
设置->评论->将较旧的评论显示在前面
,那么ajax评论成功获取到数据之后你可能会面临如何处理父级评论翻页或不翻页的情况。
所以为了方便,在添加ajax评论的时候要将较新的评论显示在前面
。
这是我基本思路下基于jQuery的代码粗略结构,稍后会一部分一部分解析:
var replyTo = '' //回复评论时候的ID
submitButton = $(".submit").eq(0), //提交评论按钮
commentForm = $("#comment-form"), //评论表单
newCommentId = "", //新评论的ID
notyf = new Notyf({
delay: 3000
}); //评论成功或者失败的提示插件
var bindButton = function()
{
//绑定“评论回复”和“取消回复”的事件
...
}
bindButton();
function commentCounts() {
//显示在评论区的评论数加一
...
}
function beforeSendComment() {
//发送前的一些处理
//比如加入一些过渡动画什么的
...
}
function afterSendComment() {
//发送后的处理
//清空replyTo变量,以及结束过渡动画、重新绑定回复按钮等等
...
}
$(commentForm).submit(function() {
//监听评论表单submit事件
var commentData = $(this).serializeArray(); //获取表单POST的数据
beforeSendComment();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: commentData,
error: function(e) {
//失败的处理
...
}
success: function(data) {
//判断评论成功或者失败
//失败显示失败提示内容,然后直接返回false
...
//发送成功进入判断
//获取新评论的id
var newComment; //新的评论的元素html内容
newCommentId = $(".comment-list", data).html().match(/id=\"?comment-\d+/g).join().match(/\d+/g).sort(function (a, b) { return a - b }).pop();
if( '' === replyTo ) {
//处理父级评论
if( !$('.comment-list').length ) {
//检查是否已有评论
//没有的话需要先嵌入评论列表的结构
...
}
else if($('.prev').length) {
//当前评论页面不在第一页
...
}
else {
//当前评论在第一页
...
}
//一些处理
...
}
else {
//处理子级评论
if ($('#' + replyTo).find('.comment-children').length) {
//当前父评论已经有嵌套的结构
//直接插入新的评论
...
}
else {
//当前父评论没有嵌套的结构
//先构建嵌套的结构再进插入子评论
...
}
}
commentCounts();//评论+1
afterSendComment(true);
notyf.confirm('评论提交成功!');//评论成功提示
}
})
return false;
})
绑定评论回复按钮
var replyTo = '';
var bindButton = function() {
//绑定“评论回复”和“取消回复”的事件
$(".comment-reply a").click(
function () {
replyTo = $(this).parent().parent().parent().parent().attr("id");
}
);
$(".cancel-comment-reply a").click(function () { replyTo = ''; });
}
绑定的回复和取消回复是这两个按钮:
replyTo
这个变量应该是需要获取到这个id:
所以$(this).parent().parent().parent().parent().attr("id");
应该根据评论结构的实际情况去修改,并且你也可以用不同的方式去拼接这个id字符串。
评论数目加一
function commentCounts() {
//显示在评论区的评论数加一
var counts = parseInt($("#response").text());
$("#response").html($("#response").html().replace(/\d+/, counts + 1));
};
$("#response")
这个选择器也是根据实际模板结构来的。
发送评论前后的处理
function beforeSendComment() {
//发送前的一些处理
//比如加入一些过渡动画什么的
//评论框提交时候的动画
submitButton.attr("disabled", true).css('cursor', 'not-allowed');
commentForm.css({ 'opacity': '.5' });
//THEME_CONFIG.THEME_URL是主题的路径
$("textarea", commentForm).css({ 'background': 'url("' + THEME_CONFIG.THEME_URL + '/assets/img/loading.gif") center center no-repeat' });
$("input,textarea", commentForm).attr('disabled', true);
}
function afterSendComment(ok) {
//发送后的处理
//清空replyTo变量,以及结束过渡动画、重新绑定回复按钮等等
//ok作为一个评论或失败的标志
//评论结束时的一些动画处理
submitButton.attr("disabled", false).css('cursor', 'pointer');
commentForm.css({ 'opacity': '1' });
$("textarea", commentForm).css({ 'background': 'initial' });
$("input,textarea", commentForm).attr('disabled', false);
if (ok) {
$("#textarea").val('');
replyTo = '';
}
bindButton();
}
主体部分解析
error
部分
beforeSendComment();
$(commentForm).submit(function() {
//监听评论表单submit事件
var commentData = $(this).serializeArray(); //获取表单POST的数据
beforeSendComment();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: commentData,
error: function(e) {
//失败的处理
//这里比较随意,比如可以直接刷新页面
console.log('Ajax Comment Error');
window.location.realod();
}
success: function(data) {
...
}
})
return false;
});
success
部分
评论失败信息提示
success: function (data) {
//评论失败提示框
if (!$('#comments', data).length) {
var msg = $('title').eq(0).text().trim().toLowerCase() === 'error' ? $('.container', data).eq(0).text() : '评论提交失败!';
notyf.alert(msg);
afterSendComment(false);
return false;
}
...
}
这个处理是在返回的数据中查找是否有下图评论错误的情况(未开启ajax评论),然后将其作为错误信息返回:
$('title').eq(0).text().trim().toLowerCase() === 'error' ? $('.container', data).eq(0).text()
父级评论处理
var newComment;
/** 获取新评论的id */
newCommentId = $(".comment-list", data).html().match(/id=\"?comment-\d+/g).join().match(/\d+/g).sort(function (a, b) { return a - b }).pop();
if(replyTo === '') {
if(!$('.comment-list').length) {
//检查是否已有评论
newComment = $("#li-comment-" + newCommentId, data);
//没有的话需要先嵌入评论列表的结构
//具体结构需要参照评论的模板而定,参照下图
$('#response').after('<div class="comment-data"><ol class="comment-list"></ol></div>');
//插入评论
$('.comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
}
else if($('.prev').length) {
//这里是当前评论不在第一页的情况
//所以这里可以进行比如跳转到第一页的操作,当然也可以进行别的操作
$('#page-nav ul li a').eq(1).click();
}
else {
//当前页面直接在最前面插入评论
newComment = $("#li-comment-" + newCommentId, data);
$('.comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
}
//页面滑动到评论列表头部
$('html,body').animate({scrollTop:$('#response').offset().top - 100},1000);
}
如下图,这个是在没有评论的时候应该嵌入的结构,具体应该根据你的模板而定
$('#response').after('<div class="comment-data"><ol class="comment-list"></ol></div>');
子级评论处理
else {
//取数据
newComment = $("#li-comment-" + newCommentId, data);
//处理子级评论
if ($('#' + replyTo).find('.comment-children').length) {
//当前父评论已经有嵌套的结构
//直接插入新的评论
$('#' + replyTo + ' .comment-children .comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
TypechoComment.cancelReply();
}
else {
//当前父评论没有嵌套的结构
//先构建嵌套的结构再进插入子评论
//插入的结构视模板具体情况而定
$('#' + replyTo).append('<div class="comment-children"><ol class="comment-list"></ol></div>');
$('#' + replyTo + ' .comment-children .comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
TypechoComment.cancelReply();
}
}
关于评论的嵌套结构是根据你的模板情况而定的,例如我的:
故我的嵌套的代码:
('#' + replyTo).append('<div class="comment-children"><ol class="comment-list"></ol></div>');
$('#' + replyTo + ' .comment-children .comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
关于TypechoComment.cancelReply();
,要注意到这是Typecho评论的取消评论回复的js代码,这会将评论框恢复到原来最底下的时候(点击回复评论会出现在那条父评论的下方)
最后部分
commentCounts();
afterSendComment(true);
notyf.confirm('评论提交成功!');
至此ajax评论的代码基本完成了
完整代码
可能和上面的有些许出入,我自己用的稍微有点修改,可以作为参考自己改成自己要的效果
并且注释没有那么详细,需要详细注释请参考上面的每部分分析
ajaxComment: function () {
var replyTo = '' //回复评论时候的ID
submitButton = $(".submit").eq(0), //提交评论按钮
commentForm = $("#comment-form"), //评论表单
newCommentId = "", //新评论的ID
notyf = new Notyf({
delay: 3000
}); //评论成功或者失败的提示插件
var bindButton = function () {
//console.log('调用bind');
$(".comment-reply a").click(function () {
replyTo = $(this).parent().parent().parent().parent().attr("id");
//console.log(replyTo);
});
$(".cancel-comment-reply a").click(function () { replyTo = ''; });
//console.log("bind button");
};
bindButton();
/**
* 评论数目加一
*/
function commentCounts() {
var counts = parseInt($("#response").text());
$("#response").html($("#response").html().replace(/\d+/, counts + 1));
};
/**
* 发送前的处理
*/
function beforeSendComment() {
submitButton.attr("disabled", true).css('cursor', 'not-allowed');
commentForm.css({ 'opacity': '.5' });
$("textarea", commentForm).css({ 'background': 'url("' + THEME_CONFIG.THEME_URL + '/assets/img/loading.gif") center center no-repeat' });
$("input,textarea", commentForm).attr('disabled', true);
}
/**
* 发送后的处理
* @param {boolean} ok
*/
function afterSendComment(ok) {
submitButton.attr("disabled", false).css('cursor', 'pointer');
commentForm.css({ 'opacity': '1' });
$("textarea", commentForm).css({ 'background': 'initial' });
$("input,textarea", commentForm).attr('disabled', false);
if (ok) {
$("#textarea").val('');
replyTo = '';
}
bindButton();
}
$("#comment-form").submit(function () {
commentData = $(this).serializeArray();
/** 准备发送数据 */
beforeSendComment();
$.ajax({
type: $(this).attr('method'),
url: $(this).attr('action'),
data: commentData,
error: function (e) {
console.log('Ajax Comment Error');
window.location.reload();
},
success: function (data) {
if (!$('#comments', data).length) {
var msg = $('title').eq(0).text().trim().toLowerCase() === 'error' ? $('.container', data).eq(0).text() : '评论提交失败!';
notyf.alert(msg);
afterSendComment(false);
return false;
}
$("input,textarea", commentForm).attr('disabled', false);
$("#textarea").val('');
var newComment;
/** 获取新评论的id */
newCommentId = $(".comment-list", data).html().match(/id=\"?comment-\d+/g).join().match(/\d+/g).sort(function (a, b) { return a - b }).pop();
/** 处理父级评论 */
if('' === replyTo) {
if(!$('.comment-list').length) {
newComment = $("#li-comment-" + newCommentId, data);
$('#response').after('<div class="comment-data"><ol class="comment-list"></ol></div>');
$('.comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
}
else if($('.prev').length) {
$('#page-nav ul li a').eq(1).click();
}
else {
newComment = $("#li-comment-" + newCommentId, data);
$('.comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
}
$('html,body').animate({scrollTop:$('#response').offset().top - 100},1000);
}
/** 处理子评论 */
else {
//取数据
newComment = $("#li-comment-" + newCommentId, data);
if ($('#' + replyTo).find('.comment-children').length) {
//已存在.comment-children 直接插入新数据
$('#' + replyTo + ' .comment-children .comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
TypechoComment.cancelReply();
}
else {
//创建一个.comment-children
$('#' + replyTo).append('<div class="comment-children"><ol class="comment-list"></ol></div>');
$('#' + replyTo + ' .comment-children .comment-list').first().prepend((newComment).addClass('animated fadeInUp'));
TypechoComment.cancelReply();
}
}
commentCounts();
afterSendComment(true);
notyf.confirm('评论提交成功!');
}
});
return false;
});
}
写在后面
本文参考过友人C的ajax评论的教程,具体是参考了下实现的思路,细节比如动画处理等有所不同。
还是那句话,添加ajax评论需要参照具体模板的结构。
请问这是哈
By Whats? at 2020-4-14 10:56 am.
学习学习OωO
By Veen Zhao at 2020-3-28 12:47 pm.
有个问题啊 就连你这边都是 评论内容空着的 提交之后只会显示提交失败,不会提示因为啥(没写内容)提交失败 一度的怀疑 $('.container', data).eq(0).text() 出问题了 有些不解
By 尚寂新 at 2019-8-11 05:32 pm.
@尚寂新
应该是这句有问题,当时这部分写的比较草率,也没用什么接口的
By Siphils at 2020-4-2 11:14 pm.
大佬OωO
By AI4U at 2019-4-26 05:48 pm.
来看大佬。
By wu先生 at 2019-3-25 09:38 pm.
大佬 二次开发评论部分 我这部分后端代码是完全重构了
如果ajax提交评论参数 我写进数据库以后 返回给ajax的参数是什么格式的
分别有哪些字段
By 怼 at 2019-3-7 12:04 pm.
@怼
后端部分重构我就不懂了,我只是监听了评论按钮的submit事件,post参数使用serializeArray()方法获取的。
By Siphils at 2019-3-7 10:01 pm.
@Siphils
已经搞定了原来直接按照正常的post提交一样重定向 然后#号到指定的li标签的id就行 然后直接定位这块html代码抓出来填进去对么
By 怼 at 2019-3-8 08:28 am.
@怼
可以这么做,也可以自己构建单条评论的Html结构插入。
By Siphils at 2019-3-8 09:09 pm.
@Siphils
刚刚细看了一下js 好像ajax没有返回参数 我现在搞不懂这个新的代码是从哪来的
By 怼 at 2019-3-8 12:18 am.
@Siphils
我想知道 ajax提交以后是抓去哪些参数生成这个html代码的
或者告诉我ajax提交以后 获得的data参数是什么样的
还是说这个新生成的评论的html代码是其他方式生成的
By 怼 at 2019-3-8 12:09 am.
By ohmyga at 2018-12-25 06:26 pm.
@ohmyga
抛砖引玉而已
不客气
By Siphils at 2018-12-25 11:52 pm.
试试你的表情好使不(´இ皿இ`)
By 洛小依 at 2018-10-29 10:27 am.
强势……
我用了比较简陋的方法,提交之后没有去获得新评论的ID,所以就不能在当前页面直接再次回复了……
By 熊猫小A at 2018-10-15 04:28 pm.
@熊猫小A
By Siphils at 2018-10-15 05:49 pm.
@Siphils
不错,造好了,感谢
By 邓先森 at 2019-4-24 09:38 pm.