ZigZagK的博客
在PJAX的基础上使用AJAX评论等功能
在PJAX的基础上使用AJAX评论等功能
2019年6月7日 01:23
Typecho
查看标签

终于把AJAX评论搞好了QwQ,效果可以在这篇文章下面评论试试(所以在本页可以随意划水2333333)。

AJAX评论

方法1:AJAX提交评论

2020.2.16 UPD:过了这么久我总算有了一点长进QAQ,现在已经基本明白了这种方法的原理。由于此方法兼容性比较好(比如不影响评论过滤插件)而且写起来更方便,所以在主题重构的时候改用了这种方式。

膜拜dalao

友人C:typecho博客实现ajax评论

其实只要对form表单提交和ajax有个大致的了解,上面这篇文章看起来就通俗易懂了QAQ。

1.关闭反垃圾保护和来源页检测

这两个功能与PJAX以及AJAX评论有冲突,需要关闭(应该是有办法支持的,不过这两个功能其实没啥用,所以关了也没事)。

可以在functions.phpthemeInit函数中加上两句话:

  • 1
  • 2
  • 3
  • 4
function themeInit($archive){ Helper::options()->commentsAntiSpam=false; //关闭评论反垃圾 Helper::options()->commentsCheckReferer=false; //关闭检查评论来源页URL是否与文章链接一致 }

这样就不用手动设置了。

2.改用AJAX提交评论

首先需要阻止原先的提交,改用AJAX来提交评论。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
//#comment-form是提交评论的form $('#comment-form').submit(function(event){ var commentdata=$(this).serializeArray(); //serializeArray()可以获取到表单数据 $.ajax({ url:$(this).attr('action'), //表单的提交链接 type:$(this).attr('method'), //表单的提交方式(post) data:commentdata, //需要提交的数据 beforeSend:function(){}, //AJAX提交前 error:function(){}, //AJAX提交失败 success:function(data){} //AJAX提交成功 }); return false; //阻止原先提交 });

如果AJAX提交成功,在success中会返回一个data,它是提交成功之后跳转到的页面的HTML代码

3.提交失败时的错误信息

提交失败分为两种:1.AJAX提交失败(比如你没网了🙃)。2.AJAX提交成功,但是被typecho拒绝(比如评论为空)。

第二种情况下,data将会返回类似这样的内容:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
<html lang="en"> <head> <meta charset="UTF-8"> <title>Error</title> <style></style> </head> <body> <div class="container"> 必须填写评论内容 </div> </body> </html>

比较有标志性的内容是<title>Error</title>,我们可以通过检测这个字符串是否存在来区分评论提交成功或失败。然后用正则表达式获取到<div class="container"></div>中的错误信息就行了。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
var error=/<title>Error<\/title>/; if (error.test(data)){ //如果存在<title>Error</title> var text=data.match(/<div(.*?)>(.*?)<\/div>/is); //text[0]是匹配结果,text[1]是第一个(.*?),text[2]是第二个(.*?) var str='发生了未知错误';if (text!=null) str=text[2]; mdui.alert(str,'评论失败'); //mdui的提示框 }

4.获取新的评论列表

如果在3中没有出现<title>Error</title>,说明评论提交成功了,这时候data将会返回手动刷新页面之后的HTML代码,现在要做的就是将提交后的评论显示出来。

一种方法是手动将这条评论插入,不过还要考虑页数变化等一系列问题所以很麻烦。还有一种方法是发起pjax请求重载页面(会打断音乐播放)。

这里提出另一种傻瓜式方法:既然有了HTML代码,我们可以直接把评论列表给替换掉,很多问题就能简单的解决了!首先先在评论列表和评论分页的外部套上一层<div>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
<div id="commentcontent"> <?php $comments->listComments(); ?> <?php if ($comments->have()){ ?> <?php $comments->pageNav('<i class="mdui-icon material-icons">&#xe314;</i>','<i class="mdui-icon material-icons">&#xe315;</i>',2,'···',array('wrapTag' => 'div','wrapClass' => 'page-navigator mdui-text-center','itemTag' => 'div','currentClass' => 'current')); ?> <?php } ?> </div>

然后就可以在提交成功之后直接将#commentcontent中的内容替换掉:

  • 1
  • 2
$('#commentcontent').html($('#commentcontent',data).html()); //$('#commentcontent',data)表示在data(HTML代码)中的新#commentcontent

最后只要写一些重载代码即可(比如评论数+1)!写重载代码时,也可以利用上述方法强行替换,虽然傻瓜式,但是效果不错。

5.定位到最新评论

这是最后一步,定位到最新评论。

根据typecho评论的特点,如果不在评论第一页发起母评论(没有回复对象,即第一层评论),提交评论之后仍会留在当前页面。这就导致出现了两种情况:

  1. 提交的评论是母评论且当前不是评论第一页。(不会跳转到最新评论)
  2. 提交的评论不是母评论,或者在第一页提交了母评论。(会跳转到最新评论)

区分这两种情况可以采用这样的方法:

  1. 检查commentdata中是否含有parent参数,如果有说明不是母评论。
  2. 检查页面中是否有评论前一页的链接,如果有说明不是评论第一页。

那么代码就是这样的:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
var target='#comments',parent=true; //默认定位回评论框 $.each(commentdata,function(i,field) {if (field.name=='parent') parent=false;}); //检查是否含有parent参数 if (!parent || !$('div.page-navigator .prev').length){ //如果不是母评论,或者当前是评论第一页 //那么只要拿出所有评论中id最大的就是最新评论 var latest=-19260817; $('#comments .mdui-panel',data).each(function(){ var id=$(this).attr('id'),coid=parseInt(id.substring(8)); if (coid>latest) {latest=coid;target='#'+id;} }); }

最后跳转到target所在位置即可:

  • 1
$('html,body').animate({scrollTop:$(target).offset().top},'fast');

总的js代码

点击展开

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
$('#comment-form').submit(function(event){ var commentdata=$(this).serializeArray(); $.ajax({ url:$(this).attr('action'), type:$(this).attr('method'), data:commentdata, beforeSend:function() {$('#commentsumbit').css('display','none');$('#commenting').css('display','block');}, error:function() {mdui.alert('发生了未知错误','评论失败');$('#commenting').css('display','none');$('#commentsumbit').css('display','block');}, success:function(data){ $('#commenting').css('display','none');$('#commentsumbit').css('display','block'); var error=/<title>Error<\/title>/; if (error.test(data)){ var text=data.match(/<div(.*?)>(.*?)<\/div>/is); var str='发生了未知错误';if (text!=null) str=text[2]; mdui.alert(str,'评论失败'); } else { //评论框复位(清空文本,刷新高度) $('#commenttextarea').val('');$('#commenttextarea').css('height',''); //评论框复位(取消回复) if ($('#cancel-comment-reply-link').css('display')!='none') $('#cancel-comment-reply-link').click(); var target='#comments',parent=true; $.each(commentdata,function(i,field) {if (field.name=='parent') parent=false;}); if (!parent || !$('div.page-navigator .prev').length){ var latest=-19260817; $('#comments .mdui-panel',data).each(function(){ var id=$(this).attr('id'),coid=parseInt(id.substring(8)); if (coid>latest) {latest=coid;target='#'+id;} }); } $('#recentcomment').html($('#recentcomment',data).html()); //更新最新评论 $('#commentsnumber').html($('#commentsnumber',data).html()); //更新评论数量 $('#commentcontent').html($('#commentcontent',data).html()); //更新评论列表 //下面是重载函数以及评论成功提示 MathJax.Hub.Typeset(document.getElementById('commentcontent')); document.querySelectorAll('#commentcontent pre code').forEach((block) => {hljs.highlightBlock(block);}); $('#commentcontent pre code').each(function(){ var lines=$(this).text().split('\n').length; var numbering=$('<ul/>').addClass('pre-numbering'); for(var i=1;i<=lines;i++) numbering.append($('<li/>').text(i)); $(this).addClass('has-numbering').parent().prepend(numbering); }); mdui.mutation();$('html,body').animate({scrollTop:$(target).offset().top},'fast'); mdui.snackbar({message:'<?php echo $this->options->commentsuccess; ?>',position:'right-bottom'}); } } }); return false; });

方法2:改造feedback

注:此方法可以方便的返回评论数据,但是兼容性较差,已经弃用。

点击展开
该方法已弃用

膜拜dalao

绛木子:不使用插件实现Ajax评论功能

QQdie:Typecho不使用插件实现Ajax评论功能

实现方法

因为我js菜爆了,网上传统的基于js的AJAX评论我并不是很能看懂QAQ。

最近发现绛木子这种比较好理解,而且容易魔改。就花了一段时间弄了一下。

的确是套(抄)上去就能用了,但是有一个很尴尬的问题……绛木子和QQdie现在用的主题都没有PJAX,而我在加上去之后和PJAX撞了……

兼容PJAX

绛木子教程里说的开启反垃圾保护是为了不验证来源页,但是不知道什么奥妙重重的原因,如果主题有PJAX,那么就算开启反垃圾保护,在切换页面之后好像依然会验证来源页,导致无法评论(或者PJAX失效)。

魔改了好久之后我还是解决不了,于是索性把:

  • 1
  • 2
  • 3
if($archive->request->get('_') != Helper::security()->getToken($archive->request->getReferer())){ $archive->response->throwJson(array('status'=>0,'msg'=>_t('非法请求'))); }

这段给删了。结果就行了……

评论@式

实现方式

这个是自己瞎搞的……原理其实特智障。

先写了两个函数:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
function GetCommentAt($coid){ $db=Typecho_Db::get();$fa=$db->fetchRow($db->select('coid,author')->from('table.comments')->where('coid = ?',$coid)); $content='<strong class="haveat"><a href="#comment-' . $fa['coid'] . '">@' . $fa['author'] .'&nbsp;</a></strong>'; return $content; } function RewriteComment($comment){ $content=convertSmilies($comment->content); //convertSmilies是表情转换 if ($comment->parent) $content=GetCommentAt($comment->parent) . $content; return $content; }

其实就是把父评论的信息从数据库里拿出来,再加到评论内容的前面。

为了将评论改为两层的,再把输出评论的部分改一下(下面给出的是框架):

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
<div id="<?php $comments->theId(); ?>"> <?php if ($comments->levels==0) { ?> <div> <!-- 对于顶级评论,评论框包住子评论 --> <?php echo RewriteComment($comments); ?> <!-- 输出转化后的评论 --> <?php if ($comments->children) { ?> <div class="comment-children"> <?php $comments->threadedComments($options); ?> </div> <?php } ?> </div> <?php } else { ?> <div> <!-- 对于非顶级评论,评论框不包住子评论 --> <?php echo RewriteComment($comments); ?> </div> <?php if ($comments->children) { ?> <div class="comment-children"> <!-- 子评论还是处在comment-children中以便定位 --> <?php $comments->threadedComments($options); ?> </div> <?php } ?> <?php } ?> </div>

然后评论就变成两层式的了,那么也就可以进行“无限”嵌套并且样式不爆炸了QAQ:

  • 1
  • 2
  • 3
  • 4
function themeInit($archive) { Helper::options()->commentsMaxNestingLevels = 19260817; //评论"无限"层 if ($archive->is('single') && $archive->request->isPost() && $archive->request->is('themeAction=comment')) ajaxComment($archive); //AJAX评论 }

评论无限加载(实验性)

效果就是将翻页改成点击加载更多。

PS:使用后发现此方法将引起一些问题,所以我就没在用了QAQ

膜拜dalao

枫叶:typecho 实现点击加载更多文章

实现方法

虽然是点击加载更多评论,但是原理和上面那篇教程里的一样。

不过因为Typecho没有单独输出评论下一页的函数,所以需要css里把除了下一页之外的东西隐藏掉……

PHP部分:

  • 1
<?php $comments->pageNav('','查看更多评论',0,'',array('wrapTag' => 'div','wrapClass' => 'page-navigator','itemTag' => '','nextClass' => 'next mdui-btn mdui-btn-block mdui-ripple mdui-color-theme-accent mdui-m-b-1')); ?>

css部分:

  • 1
div.page-navigator a:not([class~="next"]) {display:none;}

JS部分:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
$('a.next').click(function() { $this = $(this);$this.hide();$("#commenet-load").show(); var href = $this.attr('href'); if (href != undefined) { $.ajax({ url: href, type: 'get', error: function() {mdui.alert("评论加载出错了QAQ");}, success: function(data) { //把下一页网站里的#allcomment拿出来加入当前页的#allcomment var $res = $(data).find('#allcomment'); $("#allcomment").append($res);mdui.mutation(); //把点击加载更多按钮的链接换成下一页的下一页 var newhref = $(data).find('a.next').attr('href'); if (newhref != undefined) {$('a.next').attr('href', newhref);} else {$('a.next').remove();} }, complete: function() { $("#commenet-load").hide();$this.show(); } }); } return false; });

最后

其实这主要是篇给自己的总结文章,阅读过程中很有可能会觉得很混乱……有问题可以评论里提问,我有空(可能不太有,咕咕咕)会回复的QAQ。

版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处!
请不要发毫无意义或内容不文明的评论。与本文无关评论请发留言板!
极简
2021-04-17 19:48:44极简
2021-04-17 19:48:44

测试

访客
2020-07-31 20:11:57枫叶
2020-07-31 20:11:57

你那个(总的js代码)是不是用了别的jq库呀,告知下哈~

访客
2020-07-31 20:46:50ZigZagK
2020-07-31 20:46:50
@枫叶 

类似mdui.alert的函数都是mdui库中的,需要自行替换,或者引入mdui库

博主
2020-07-31 19:38:49枫叶
2020-07-31 19:38:49

我也想试试你这个ajax评论效果~ |´・w・)ノ

访客
2020-03-28 14:23:49Veen Zhao
2020-03-28 14:23:49

过来取取经 向大佬低头

访客
2020-03-16 11:46:23ZigZagK
2020-03-16 11:46:23

趁没人发现来测试一波Twemoji 我的滑稽会冒汗
😀😁😂🤣😃😄😅😆😉😊😋😎😍😘🥰😗😙😚🙂🤗🤩🤔🤨😐😑😶🙄😏😣😥😮🤐😯😪😫😴😌😛😜😝🤤😒😓😔😕🙃🤑😲☹️🙁😖😞😟😤😢😭😦😧😨😩😬😰😱🥵🥶😳🤪😵😡😠🤬😷🤒🤕🤢🤮🤧😇🤠🥳🥴🥺🤥🤫🤭🧐🤓👻🤲👐🙌👏🤝👍👎👊✊🤛🤜🤞✌️🤟🤘👌👈👉👆👇✋🤚🖐🖖👋🤙💪🦵🦶🖕✍️🙏👀👴🐴🍋

博主
2020-03-24 16:37:03Bhao
2020-03-24 16:37:03
@ZigZagK 

时隔几日,我发现了! 滑天下之大稽

访客
2020-03-24 16:45:39ZigZagK
2020-03-24 16:45:39
@Bhao 

这都被你发现了

博主
2019-12-18 14:21:57Zapic
2019-12-18 14:21:57

我自己做的时候实践了一下,发现那些AJAX评论...
就是在主题里塞了个插件的感觉... 无奈.jpg

实际上,评论来源页错误的问题,我们只需要把评论来源页验证方法找出来,再在PJAX翻页时给重载就好了.
结果真的找到了:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
//打乱 Typecho_Common::shuffleScriptVar( //变token $this->security->getToken( //获得当前页url $this->request->getRequestUrl() ) );

这样大概能拿到一串这样的东西:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
(function () { var _yDX = //'CV' '240'+//'QIL' '2'+'772'//'pO' +//'Jf' '1'+'b7'//'pG' +//'J' '607'+/* 'D'//'D' */''+'e'//'q' +'74'//'H' +//'c' 'c'+'519'//'b' +/* 'O0F'//'O0F' */''+'0'//'4c' +''///*'2'*/'2' +'a'//'8Dt' +//'f' 'f'+'e0'//'jIy' +//'0I' '0I'+'6Zu'//'6Zu' +'6e4'//'Cv' +''///*'U3'*/'U3' +//'Tm' 'fbe'+''///*'l'*/'l' +//'Uf9' 'e'+'2M'//'2M' +//'WUF' '2', _4pkJK = [[16,17],[24,26],[24,27],[31,33]]; for (var i = 0; i < _4pkJK.length; i ++) { _yDX = _yDX.substring(0, _4pkJK[i][0]) + _yDX.substring(_4pkJK[i][1]); } return _yDX; })();

然后参考默认模板,发现他们是通过在提交表单时把这个token放进一个_的项里同评论一起提交的.
啥?
那就很好办了.
在comments.php里把token一起输出到pjax容器里,就可以随pjax重载_的内容了.

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
//输出token <?php if ($this->options->commentsAntiSpam && $this->is('single')) {?> <!-- AntiSpam --> <script> $$("#<?php echo $this->respondId;?>").append("<input type=\"hidden\" id=\"anti-spam-token\" name=\"_\" value=\"\" />"); $$(document).one("keyup touchstart mousemove",function(){ var anti_token = <?php echo Typecho_Common::shuffleScriptVar($this->security->getToken($this->request->getRequestUrl()));?>; $$("#anti-spam-token").val(anti_token); }); </script> <?php } ?>

从而避免在主题里塞插件这种不优雅也兼容性暴跌的操作.
奥妙重重

访客
2019-12-18 14:22:47Zapic
2019-12-18 14:22:47
@Zapic 


内容全乱了(
恐惧

访客
2019-12-18 22:28:26ZigZagK
2019-12-18 22:28:26
@Zapic 

要么开个issue吧,评论太复杂解析跪了 无奈.jpg
(但是我快高考了可能没时间看,先开着吧QAQ,咕咕咕)

博主
2019-12-20 17:10:53Zapic
2019-12-20 17:10:53
@ZigZagK 

这个方法是彻底的大改,前端后端一起砍,开个issue可能有一种...
呃...
"你喜欢吃荔枝,我却告诉你梨子更好吃"的意思.
而且我估计解析炸了是因为我写代码块时少了几个顿点(

访客
2019-07-13 21:33:06ohmyga
2019-07-13 21:33:06

居然资瓷ajax评论了 赞 |´・w・)ノ

访客
2019-07-13 21:33:50ohmyga
2019-07-13 21:33:50
@ohmyga 

好高级的说((

访客
2019-07-14 14:53:33ZigZagK
2019-07-14 14:53:33
@ohmyga 

Dalao您的Blog不是早就有了吗QAQ
(其实现在还有bug一堆 倍感压力

博主
2019-07-15 10:26:06ohmyga
2019-07-15 10:26:06
@ZigZagK 

awsl 我是直接发送ajax请求再刷新页面的 你这直接插入评论 太高级了(

访客
2019-07-14 23:07:42ZigZagK
2019-07-14 23:07:42
@ZigZagK 

啊我这垃圾语文……我是说我的还有bug一堆QAQ

博主
2019-07-12 08:36:12初夏阳光
2019-07-12 08:36:12

测试一下 向大佬低头

访客
2019-06-17 20:39:53可耐的菊花茶
2019-06-17 20:39:53

前排兹瓷zigzag胖qwq

访客
2019-06-22 20:48:33ZigZagK
2019-06-22 20:48:33
@可耐的菊花茶 

你好好学竞赛去 快吃药

博主
2019-06-16 09:28:52ZigZagK
2019-06-16 09:28:52

测试一下 无奈.jpg

博主
本文写于 2128 天前,最后更新于 889 天前。
部分信息可能已经过时,博主也可能已经无法对其内容进行解答。
OK