0.Problem description
After introducing asynchronously loaded scripts such as PJAX / InstantClick, the following exception occurs when switching pages:
- Unable to click the "Reply" button;
- The comment fails. Return (refresh the page) and comment again normally.
The problem with Question 2 is that Typecho's "check whether the comment source page URL is consistent with the article link" and "anti-spam protection" are not necessarily compatible with the theme, PJAX, and InstantClick settings. The problem with Question 1 is that Typecho's settings in <head>
The content of TypechoComment
is output in TypechoComment
, while PJAX/InstantClick generally only overloads <body>
.
1.Solution
1.1, Question 1
First, turn off "Check whether the comment source page URL is consistent with the article link" and "Anti-Spam Protection" in the Typecho backend under Settings-Comments-Comment Submission.
I know that after closing, robots will spam comments, so I recommend using comment verification codes or comment anti-spam plug-ins, such as the reCAPTCHAv3 plug-in that I have used for a long time.
Recently, I discovered that there was something wrong with the ReCaptcha mirror site, either "Unable to connect to ReCaptcha" or "You have been judged to be a robot", making it almost unusable, so I changed it to Cloudflare Turnstile. For application and configuration, please refer to the article on this site:
(not written yet)
Next up is PJAX/InstantClick compatible:
If using PJAX, edit line 50 of /var/Widget/Security.php
to read:
1 | public function getTokenUrl($path) { $parts = parse_url($path); $params = array(); if (!empty($parts['query'])) { parse_str($parts['query'], $params); } $params['_'] = $this->getToken(preg_replace('/\?_pjax=%23body/', '', $this->request->getRequestUrl())); $parts['query'] = http_build_query($params); return Typecho_Common::buildUrl($parts); } |
Then add PJAX/InstantClick's refresh function (the function executed after the page changes):
1 | var now_url_comment = location.href + ' '; history.replaceState(null,'',now_url_comment.replace('/comment ','')); |
That will solve the problem.
1.2, Question 2
There are two ideas to solve the JavaScript loading problem (reply button problem):
- Reload function when updating the page;
- Directly delete the comment function that comes with Typecho and modify it to a function that adapts to your own template.
1.2.1, Idea 1
The first method is more general, just add when outputting the comment:
1 | <script>respondId = '<?php $this->respondId(); ?>';</script> |
If the template doesn't have $this
here (like mine), it can be obtained with JS. For example the comment form is:
1 | <div id="<?php $this->respondId(); ?>" class="response-box"> ... </div> |
The above output code can be changed to:
1 | <script>respondId = document.getElementsByClassName("response-box")[0].id;</script> |
No matter which method is used, the responseId
in the page will be loaded into a JS variable after output, so just add it to the refresh function of PJAX / InstantClick:
1 | 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(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(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; } }; |
If it still doesn't work, you can modify it slightly according to your own template content.
1.2.2, Idea 2
I don’t really like that when Typecho replies to a comment, it appends
the comment box below the topic to be replied to, so I directly delete the built-in comment function, that is, put the <head>
part:
1 | $this->header(''); |
Change to:
1 | $this->header('commentReply='); |
Of course, I recommend getting rid of a lot of clutter in the head as well:
1 | $this->header('generator=&template=&pingback=&xmlrpc=&wlw=&atom=&rss1=&rss2=&commentReply='); |
Then add JS to the comment output code:
1 | <script type="text/javascript"> (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; } }; })(); </script> |
如果不需要上述的评论框移动,只需删去comment.appendChild(response);
即可。
这里使用了$this->responseId
,如果没有这个值同样可以用 JS 动态获取:
1 | <script type="text/javascript"> (function () { responseId = document.getElementsByClassName("response-box")[0].id; 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); this.dom('cancel-comment-reply-link').style.display = ''; if (null != textarea && 'text' == textarea.name) { textarea.focus(); } return false; }, cancelReply : function () { var response = this.dom(responseId), input = this.dom('comment-parent'); if (null != input) { input.parentNode.removeChild(input); } this.dom('cancel-comment-reply-link').style.display = 'none'; return false; } }; })(); </script> |
Solved perfectly.
Fix Comment Problem when Using PJAX or InstantClick on Typecho
Comments