为 Typecho 主题开启 Ajax 评论真正实现全站无刷

4 年前(已编辑)
1155
2
这篇文章上次修改于 3 年前,可能部分内容已经不适用,如有疑问可询问作者。

既然做了,就要做得绝。既然开了 Pjax,那就要真正实现全站无刷。

前言

在实现评论 Ajax 之前,我在网上也找了几篇教程学习了一下,大部分都是用 jQuery 实现的,但是并不是每个主题都会引入 jQuery 的,其次为了实现 Ajax 而引入 jQuery 并不值得去这么做。所以我选择用原生的方式去实现。

实现过程

首先,我们要知道表单提交的时候,浏览器发起了什么请求,请求报文有哪些。在评论时,浏览器会发起 POST 请求,请求体中的数据有评论者的信息(name, url, mail, text),注意需要判断用户是否登陆,如果登陆,就只有 text.

可以用 dom 元素的 value 属性获取用户表单的数据,然后发起 ajax 请求。请求地址这可以从表单上的 action 属性获取。当前我们还需要将响应报文中的 dom 元素提取需要的部分,进行替换。还可以加点过度动画等等,需要注意的是别忘了重载某些方法,和阻止默认提交事件。

下面贴上完整代码

class Comment {

    static comment_init() {
      const commentsReply = document.querySelectorAll('span.comment_reply > a')
      const replyForm = document.querySelector('.reply')
      const isComment = document.querySelector('.post-form.is-comment')
      for (let el of commentsReply) {
        el.addEventListener('click', e => {
          // 给恢复按钮绑定事件 获取parent-id
          const href = e.target.getAttribute('href')
          window.parentId = href.match(/replyTo=(\d+)/)[1]
          // 弹出回复框
          replyForm.removeAttribute('style')
          if (isComment.classList.contains('active')) isComment.classList.remove('active');
          setTimeout(() => {
            document.getElementById('cancel-comment-reply-link').addEventListener('click', () => {
              replyForm.style.display = 'none';
            })
          })
        })
      }
    }
  }

  Comment.comment_init()

  // ajax 提交评论实现方法

  // 阻止默认事件
  const form = document.getElementById('comment-form')
  form.addEventListener('submit', function (e) {
    e.preventDefault();
    post_by_ajax(e, '#comment-form')
  });

  const reply_form = document.querySelector('.reply_form')
  reply_form.addEventListener('submit', function (e) {
    e.preventDefault();
    post_by_ajax(e, '.reply_form', true)
  });


  // ajax 提交
  function post_by_ajax(e, sel, reply = false) {
    const isComment = document.querySelector('.post-form.is-comment')
    const commentForm = document.querySelector(sel)
    const post_url = e.target.getAttribute('action')
    const cookie = document.cookie
    const referer = window.location.href
    const domParser = new DOMParser()
    const dom = str => domParser.parseFromString(str, 'text/html')

    // 如果是管理员登陆
    if (!document.querySelector('#comment-form #author')) {
      const text = commentForm.querySelector('#text').value
      let data = null

      if (reply) {
        data = {
          text, parent: window.parentId
        }
      } else {
        data = {
          text
        }
      }
      ks.ajax({
        url: post_url,
        method: 'POST',
        data,
        success(res) {
          const responseDOM = dom(res.responseText)

          try {
            isComment.classList.contains('active') ? isComment.classList.remove('active') : false
            const needPartten = responseDOM.querySelector('.comment-list').innerHTML
            needPartten === document.querySelector('.comment-list').innerHTML ? ks.notice("请等待审核哦 φ(>ω<*) ", {
              color: "green",
              time: 1000
            }) : (document.querySelector('.comment-list').innerHTML = needPartten, ks.notice("评论成功了 (〃'▽'〃)", {
              color: "green",
              time: 1000
            }), (reply ? false : window.scrollSmoothTo(document.body.scrollHeight || document.documentElement.scrollHeight)))

          } catch (e) {
            ks.notice(responseDOM.querySelector('.container').innerText, {
              color: "red",
              time: 1500
            })
          }
          Comment.comment_init()
        },
        failed(res) {
          console.log(res)
          ks.notice("(;´д`)ゞ 失败了", {
            color: 'red',
            time: 1500
          })
        }
      })
    } else {
      const author = commentForm.querySelector('#author').value
      const mail = commentForm.querySelector('#mail').value
      const url = commentForm.querySelector('#url').value
      const text = commentForm.querySelector('#text').value

      if (reply) {
        data = {
          author, mail, url, text, parent: window.parentId
        }
      } else {
        data = {
          author, mail, url, text,
        }
      }

      ks.ajax({
        method: "POST",
        url: post_url,
        data,
        success(res) {
          const responseDOM = dom(res.responseText)
          isComment.classList.contains('active') ? isComment.classList.remove('active') : false
          try {
            const needPartten = responseDOM.querySelector('.comment-list').innerHTML
            needPartten === document.querySelector('.comment-list').innerHTML ? ks.notice("请等待审核哦 φ(>ω<*) ", {
              color: "green",
              time: 1000
            }) : (document.querySelector('.comment-list').innerHTML = needPartten, ks.notice("评论成功了 (〃'▽'〃)", {
              color: "green",
              time: 1000
            }), (reply ? false : window.scrollSmoothTo(document.body.scrollHeight || document.documentElement.scrollHeight)))
            Comment.comment_init()
          } catch (e) {
            ks.notice(responseDOM.querySelector('.container').innerText, {
              color: "red",
              time: 1500
            })
          }

        }
        ,
        failed(res) {
          console.log(res)
          ks.notice("(;´д`)ゞ 失败了", {
            color: 'red',
            time: 1500
          })
        }
      })

    }
    return false
  }

后续说明

在完成 Ajax 请求后,我做了两件事。

第一,判断是否评论成功,如果失败则弹出信息框,说明原因。成功则重载方法,并用平滑移动等方法跳转到新评论的地方。

第二,评论成功后,收回评论框。

由于在项目中使用了 Kico Style ,所以 Ajax 请求由 ks.ajax() 实现,原生方法亦可。

使用以上实现方法的主题:Paul

评论区加载中...