终于把AJAX评论搞好了QwQ，效果可以在这篇文章下面评论试试（所以在本页可以随意划水2333333）。AJAX评论方法1：AJAX提交评论2020.2.16 UPD：过了这么久我总算有了一点长进QAQ，现在已经基本明白了这种方法的原理。由于此方法兼容性比较好（比如不影响评论过滤插件）而且写起来更方便，所以在主题重构的时候改用了这种方式。膜拜dalao友人C：typecho博客实现ajax评论其实只要对form表单提交和ajax有个大致的了解，上面这篇文章看起来就通俗易懂了QAQ。1.关闭反垃圾保护和来源页检测这两个功能与PJAX以及AJAX评论有冲突，需要关闭（应该是有办法支持的，不过这两个功能其实没啥用，所以关了也没事）。可以在functions.php的themeInit函数中加上两句话：function themeInit($archive){ Helper::options()->commentsAntiSpam=false; //关闭评论反垃圾 Helper::options()->commentsCheckReferer=false; //关闭检查评论来源页URL是否与文章链接一致 }这样就不用手动设置了。2.改用AJAX提交评论首先需要阻止原先的提交，改用AJAX来提交评论。//#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将会返回类似这样的内容： Error 必须填写评论内容 比较有标志性的内容是 Error ，我们可以通过检测这个字符串是否存在来区分评论提交成功或失败。然后用正则表达式获取到 中的错误信息就行了。var error=/ Error<\/title>/; if (error.test(data)){ //如果存在 Error 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中没有出现 Error ，说明评论提交成功了，这时候data将会返回手动刷新页面之后的HTML代码，现在要做的就是将提交后的评论显示出来。一种方法是手动将这条评论插入，不过还要考虑页数变化等一系列问题所以很麻烦。还有一种方法是发起pjax请求重载页面（会打断音乐播放）。这里提出另一种傻瓜式方法：既然有了HTML代码，我们可以直接把评论列表给替换掉，很多问题就能简单的解决了！首先先在评论列表和评论分页的外部套上一层 ： <?php $comments->listComments(); ?> <?php if ($comments->have()){ ?> <?php $comments->pageNav('','',2,'···',array('wrapTag' => 'div','wrapClass' => 'page-navigator mdui-text-center','itemTag' => 'div','currentClass' => 'current')); ?> <?php } ?> 然后就可以在提交成功之后直接将#commentcontent中的内容替换掉：$('#commentcontent').html($('#commentcontent',data).html()); //$('#commentcontent',data)表示在data(HTML代码)中的新#commentcontent最后只要写一些重载代码即可（比如评论数+1）！写重载代码时，也可以利用上述方法强行替换，虽然傻瓜式，但是效果不错。5.定位到最新评论这是最后一步，定位到最新评论。根据typecho评论的特点，如果不在评论第一页发起母评论（没有回复对象，即第一层评论），提交评论之后仍会留在当前页面。这就导致出现了两种情况：提交的评论是母评论且当前不是评论第一页。（不会跳转到最新评论）提交的评论不是母评论，或者在第一页提交了母评论。（会跳转到最新评论）区分这两种情况可以采用这样的方法：检查commentdata中是否含有parent参数，如果有说明不是母评论。检查页面中是否有评论前一页的链接，如果有说明不是评论第一页。那么代码就是这样的：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所在位置即可：$('html,body').animate({scrollTop:$(target).offset().top},'fast');总的js代码[panel title="点击展开"]$('#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=/ 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=$(' ').addClass('pre-numbering'); for(var i=1;i<=lines;i++) numbering.append($(' ').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; });[/panel]方法2：改造feedback注：此方法可以方便的返回评论数据，但是兼容性较差，已经弃用。[panel title="点击展开" summary="该方法已弃用"]膜拜dalao绛木子：不使用插件实现Ajax评论功能。QQdie：Typecho不使用插件实现Ajax评论功能。实现方法因为我js菜爆了，网上传统的基于js的AJAX评论我并不是很能看懂QAQ。最近发现绛木子这种比较好理解，而且容易魔改。就花了一段时间弄了一下。的确是套（抄）上去就能用了，但是有一个很尴尬的问题……绛木子和QQdie现在用的主题都没有PJAX，而我在加上去之后和PJAX撞了……兼容PJAX绛木子教程里说的开启反垃圾保护是为了不验证来源页，但是不知道什么奥妙重重的原因，如果主题有PJAX，那么就算开启反垃圾保护，在切换页面之后好像依然会验证来源页，导致无法评论（或者PJAX失效）。魔改了好久之后我还是解决不了，于是索性把：if($archive->request->get('_') != Helper::security()->getToken($archive->request->getReferer())){ $archive->response->throwJson(array('status'=>0,'msg'=>_t('非法请求'))); }这段给删了。结果就行了……[/panel]评论@式实现方式这个是自己瞎搞的……原理其实特智障。先写了两个函数：function GetCommentAt($coid){ $db=Typecho_Db::get();$fa=$db->fetchRow($db->select('coid,author')->from('table.comments')->where('coid = ?',$coid)); $content='@' . $fa['author'] .' '; return $content; } function RewriteComment($comment){ $content=convertSmilies($comment->content); //convertSmilies是表情转换 if ($comment->parent) $content=GetCommentAt($comment->parent) . $content; return $content; }其实就是把父评论的信息从数据库里拿出来，再加到评论内容的前面。为了将评论改为两层的，再把输出评论的部分改一下（下面给出的是框架）：<div id="<?php $comments->theId(); ?>"> <?php if ($comments->levels==0) { ?> <?php echo RewriteComment($comments); ?> <?php if ($comments->children) { ?> <?php $comments->threadedComments($options); ?> <?php } ?> <?php } else { ?> <?php echo RewriteComment($comments); ?> <?php if ($comments->children) { ?> <?php $comments->threadedComments($options); ?> <?php } ?> <?php } ?> 然后评论就变成两层式的了，那么也就可以进行“无限”嵌套并且样式不爆炸了QAQ：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部分：<?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部分：div.page-navigator a:not([class~="next"]) {display:none;}JS部分：$('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。