再一次的给站点添加 ajax 评论体验
我所周知,在全站 pjax 的情况下实现 ajax 评论是一个很炸脑袋的事情。经过了很多试验踩坑之后,最终还是采用了他的方案。
https://eriri.ink/archives/typecho-ajaxcomment.html
其实看完他的这个之后,看着注释标的也挺比较全,然后就用了这套方案,然后进行了一顿魔改之后应用到了我的站上。这套的优势就是用 Typecho 原生的接口,评论处理什么的直接使用原有的插件就可以。
下面是魔改之后的代码,需要引用 jq 库!
本文需要阅读者拥有一定的折腾能力,纯小白、纯萌新建议绕路
function ajax_edit_ver () {
var replyTo = '' //回复评论时候的ID
submitButton = $(".submit").eq(0), //提交评论按钮
commentForm = $("#comment-form"), //评论表单
newCommentId = "", //新评论的ID
//console.log('调用bind');
$(".js-comment-reply a").click(function () {
//console.log('已触发点击回复');
replyTo = $(this).parent().parent().attr("id");
//console.log(replyTo);
});
$(".js-cancel-comment-reply a").click(function () { /*console.log('已触发取消回复')*/;replyTo = ''; });
//console.log("bind button");
/**
* 评论数目加一
*/
function commentCounts() {
var counts = parseInt($("#comment-ajax-plus-plus").text());
$("#comment-ajax-plus-plus").html($("#comment-ajax-plus-plus").html().replace(/\d+/, counts + 1));
};
/**
* 发送前的处理
*/
function beforeSendComment() {
submitButton.attr("disabled", true).css('cursor', 'not-allowed');
commentForm.css({ 'opacity': '0.5' });
$("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 = '';
}
//console.log('调用bind');
$(".comment-reply a").click(function () {
replyTo = $(this).parent().parent().attr("id");
//console.log(replyTo);
});
$(".cancel-comment-reply a").click(function () { replyTo = ''; });
//console.log("bind button");
}
$("#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() : '评论提交失败!请检查你输入的内容!';
swal("返回错误", msg, "error");
//alert(msg);
//console.log(msg);
afterSendComment(false);
return 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) {
/** 本博含有零评论提示,本部分无用已精简(无评论结构需与评论单元格式基本一致) */
}
else if($('.prev').length) {
$('.page-navigator li a').eq(1).click();
}
else {
newComment = $("#li-comment-" + newCommentId, data);
$('.comment-list').first().prepend((newComment));
$('#no-comment-placeholder').remove();
}
//$('html,body').animate({scrollTop:$('#response').offset().top - 100},1000);
}
/** 处理子评论 */
else {
//取数据
newComment = $("#li-comment-" + newCommentId, data);
if ($('#li-' + replyTo).find('.comment-children').length) {
//已存在.comment-children 直接插入新数据
$('#li-' + replyTo + ' .comment-children .comment-list').prepend((newComment));
TypechoComment.cancelReply();
}
else {
//创建一个.comment-children
$('#li-' + replyTo).append('<div class="comment-children"><ol class="comment-list"></ol></div>');
$('#li-' + replyTo + ' .comment-children .comment-list').first().prepend((newComment));
TypechoComment.cancelReply();
}
}
commentCounts();
afterSendComment(true);
//alert('评论提交成功!如首次在本站评论,需要等待审核!');
swal("评论成功", "如首次在本站评论,需要等待审核!", "success");
}
});
return false;
});
}
ajax_edit_ver ();
讲解
L3-L4:需要自行按照主题模板进行适配。
L9:需要定位父元素。一个.parent()
相当于往前推一个标签。
<li id="li-comment-107" class="comment-container comment-even ">
<div id="comment-107">
<img src="https://gravatar.loli.net/avatar/eda06ebc12b16108a945e2755c5df7bc?s=35&r=G&d=identicon" class="avatar">
<div class="reply js-comment-reply"><a href="//shangjixin.com/links.html?replyTo=107#respond-page-64" rel="nofollow" onclick="return TypechoComment.reply('comment-107', 107);">回复</a></div>
<div class="info">
<div class="name"><a href="https://example.com/" rel="external nofollow">尚寂新</a> <span class="sdblue" aria-hidden="true" style="cursor:pointer;" title="喵~">博主</span></div>
<div class="time">17-10-03 07:37 @ Android 5.1</div>
<div class="desc"><p> 友情链接一直招收中哦</p></div>
</div>
</div>
</li>
就比如说,我的评论结构是这样的,第四行中有一个回复按钮,抓住它。包裹着他的<div>
标签需要定义class
(不要定义为id
,因为id
在一个页面只能有一个同名的)然后把这个在主代码那块 L7 那块标好,定位回复按钮。本示例往外推 3 层就可以定位到comment-107
了,所以说.parent()
要写三个。L12 同理,只是不需要判断.parent()
。
评论数+1:可以参照本博,把数字拿标签包起来,然后换进去就行了(这个应该不用多解释)。
发送前的处理:无关紧要,主要就是变一些 css 什么的,如果不需要,L27-L29 均可注释掉。
发送后的处理:同上。但 L37-L40 才只是包括样式的变换。L41-L51 均为复位的操作。
L72-L74 以及 L117-L118:评论失败/成功的通知。swal()
是一个 jq 提示框插件,如果不想装的话可以换成 js 的原生方式,或者换用其他提示框。(更换的话不用多说了吧)。
L87:内容我已经省略,需要的话找下原帖就可以(在文章开始的时候引用过一遍(偷懒.jpg))(提示:他那边行数跟我这边肯定不是对应的,因为我这个魔改的跟原版有区别)需要像我那么弄的话,在commments.php
里判断有无评论的地方加进去个else
之后,依照评论结构为模板(如上方L9的讲解那样),不要变动外两层,基本上都能认出来。
我的子评论嵌入位置为上面 L9 实例那个 L8 的后面,这样更有利于 ajax 评论跟页面初加载的时候效果一致,但也需要用 css 把子评论的效果改一下 实际上就是套娃样式,css 改一改就不一样了
L124:所谓的 ajax/pjax 回调代码。这个实际意义是把上面那一大堆都给执行一遍。就这样
差不多就是这样。如果你实在是再想偷懒的话(我刚开始就是这么搞的,当时脑袋都折磨炸了,js 一点基础都没有)L81-L144 全部删掉即可。删掉之后,即使评论成功了,评论内容也不会更新跟上来(实际上内容以及成功添加,入库了),但页面是无刷新的。
L90:评论分页存在时,往前翻一页,让评论者看到新评论,同样需要进行适配。不需要的话同样可以打上注释,或者是没开评论分页的话,这行其实都可以不太用管。
嗯,如果站点存在全站 ajax 或 pjax 的话, 刷新区内原生评论定位用的 js (TypechoComment
)还是要添加的。具体如下(别放在单独的 .js 静态文件里,因为里面有 php 代码)
(function () {
window.TypechoComment = {
dom : function (id) {
return document.getElementById(id);
},
create : function (tag, attr) {
var el = document.createElement(tag);
for (var key in attr) {
el.setAttribute(key, attr[key]);
}
return el;
},
reply : function (cid, coid) {
var comment = this.dom(cid), parent = comment.parentNode,
response = this.dom('<?php echo $this->respondId(); ?>'),
input = this.dom('comment-parent'),
form = 'form' == response.tagName ? response : response.getElementsByTagName('form')[0],
textarea = response.getElementsByTagName('textarea')[0];
if (null == input) {
input = this.create('input', {
'type' : 'hidden',
'name' : 'parent',
'id' : 'comment-parent'
});
form.appendChild(input);
}
input.setAttribute('value', coid);
if (null == this.dom('comment-form-place-holder')) {
var holder = this.create('div', {
'id' : 'comment-form-place-holder'
});
response.parentNode.insertBefore(holder, response);
}
comment.appendChild(response);
this.dom('cancel-comment-reply-link').style.display = '';
if (null != textarea && 'text' == textarea.name) {
textarea.focus();
}
return false;
},
cancelReply : function () {
var response = this.dom('<?php echo $this->respondId(); ?>'),
holder = this.dom('comment-form-place-holder'),
input = this.dom('comment-parent');
if (null != input) {
input.parentNode.removeChild(input);
}
if (null == holder) {
return true;
}
this.dom('cancel-comment-reply-link').style.display = 'none';
holder.parentNode.insertBefore(response, holder);
return false;
}
};
})();
好的 差不多就是这样了qwq
我之所以不用纯js的ajax评论,就是因为没办法准确输出评论拦截插件的报错信息
我也是这么实现的,新评论就在301返回里取,旧评论会取主评论的当前评论页进行二次请求,对于回复一页之前的评论,都会有个二次请求,因为301只有第一页的评论