Typecho 文档:文章(日志)段落的点评(评论)功能

2023-08-06

适用程序:Typecho
程序版本:1.2.1
文档作者:Lopwon
作者博客:Lopwon.com
发布页面:Lopwon.com/3508.html

注意:此文档源于作者在博客改造中的一些经验总结,转载还请署名。

敬告:此文档操作涉及程序核心文件的修改,作者不对你在使用中产生的任何问题造成的不良后果,承担责任。

文档说明

在 Typecho 论坛上看到一则 #需求帖子,虽然年代有些久远,源自2014年,但是觉得想法很是不错,就尝试着去实现它,在 ChatGPT 的协助下,完成了大部分的功能,除了没有涉及数据库外,基本满足了帖子提到的需求。

暂时没有做样式上优化,之后可能会做成插件,继续新增一些其他功能,比如:点击段落引用时,以弹窗的方式加载评论区;能够区分引用的和点评的内容;能够平滑滚动到锚点;引用段落显示小图标;段落被点评的次数……等等。

工作原理:Typecho 默认有 autoP 功能,以此作为段落的划分,这样就能对每个 <p> 添加 id 标记,用于引用和定位,另外,功能实现上也加入常用的引用元素 <blockquote>

主要功能:

1. 点击段落后,将该段落的截断内容(或图片)插入评论内容中,并跳至评论区。

2. 提交评论后,点击点评时引用的段落,也会跳至文章对应的段落。

3. 要注意的是,使用段落点评功能后,文章请勿再新增段落,否则,已提交引用且点评的段落会和编辑后文章段落的 id 对不上。当然,可以在保持段落位置和数量不变的情况下,对文章内容文字做编辑。

使用方法

A 涉及文件

usr/themes/***/footer.php

B 改造文件

作用:给文章段落添加点评功能

1. 后台》设置》评论,在(允许使用的HTML标签和属性)项里输入 <img src="">

2. 以上操作的作用是:让评论内容解析输出图片。

3. 打开你在用主题下的文件 footer.php 找个合适的位置添加以下代码(留意注释,以及按需修改):

    <script>

    const container = document.querySelector('.post-content'); // 获取文章内容的容器元素 post-content(根据自己的主题修改)
    const paragraphs = container.querySelectorAll('p'); // 获取文章内容所有的 <p> 元素(依据 Typecho 默认的 autoP 功能)
    const blockquotes = container.querySelectorAll('blockquote'); // 获取文章内容所有的 <blockquote> 元素(即:引用)
    const comments = document.querySelectorAll('.comment-content p'); // 获取评论内容所有的 <p> 元素(根据自己的主题修改,通常为 .comment-content p)
    const textarea = document.getElementById('textarea'); // 获取评论内容的表单 <textarea> (根据自己的主题修改,通常为 textarea)

    paragraphs.forEach((p, index) => { // 遍历文章内容所有 <p> 元素,并分配 id
        const id = `p${index + 1}`; // 自定义 p 为前缀
        p.setAttribute('id', id); // 添加 id

        const imgElement = p.querySelector('img'); // 查找 <p> 元素中的 <img> 元素

        if (imgElement) { // 如果 <p> 包含 <img> 元素,则将图片信息添加到 <textarea>
            const imageSrc = imgElement.getAttribute('src'); // 获取图片的 src 属性
            const imageAlt = imgElement.getAttribute('alt'); // 获取图片的 alt 属性

            p.addEventListener('click', () => { // 添加点击事件处理程序
                const imgTag = `<img src="${imageSrc}" alt="${imageAlt}" />`; // 添加到 <textarea> 的内容结构,如果不想显示图片,则改为 `${imageAlt}`(即:只显示 alt 部分)
                addToTextarea(id, imgTag); // 添加标记信息,并传递给 addToTextarea 函数
                focusTextarea(); // 调用 focusTextarea() 方法,使 <textarea> 获取焦点
                textarea.scrollIntoView({ behavior: 'smooth' }); // 滚动到 <textarea> 的位置
            });
        } else { // 如果 <p> 不包含 <img> 元素,则将文本内容添加到 <textarea>
            p.addEventListener('click', () => { // 添加点击事件处理程序
                const content = p.textContent; // 定义内容
                const maxLength = 32; // 设置截取的最大字数
                const truncatedContent = truncateString(content, maxLength); // 截断后的内容
                addToTextarea(id, truncatedContent); // 添加标记信息,并传递给 addToTextarea 函数
                focusTextarea(); // 调用 focusTextarea() 方法,使 <textarea> 获取焦点
                textarea.scrollIntoView({ behavior: 'smooth' }); // 滚动到 <textarea> 的位置
            });
        }
    });

    blockquotes.forEach((blockquote, index) => { // 遍历所有的 <blockquote> 元素,并分配 id
        const id = `blockquote${index + 1}`; // 自定义 blockquote 为前缀
        blockquote.setAttribute('id', id); // 添加 id

        blockquote.addEventListener('click', () => { // 添加点击事件处理程序
            const content = blockquote.textContent // 定义内容
            const maxLength = 32; // 设置截取的最大字数
            const truncatedBlockquote = truncateString(content, maxLength); // 截断后的内容
            addToTextarea(id, truncatedBlockquote); // 添加标记信息,并传递给 addToTextarea 函数
            focusTextarea(); // 调用 focusTextarea() 方法,使 <textarea> 获取焦点
            textarea.scrollIntoView({ behavior: 'smooth' }); // 滚动到 <textarea> 的位置
        });
    });

    function truncateString(str, maxLength) { // 截取字符串到指定长度
        if (str.length > maxLength) { // 截取内容
            return str.substring(0, maxLength) + '...';  // 输出内容,多出的以 ... 代替
        }

        return str;
    }

    function focusTextarea() { // 使 <textarea> 获取焦点的函数
        textarea.focus();
    }

    function addToTextarea(paragraphId, content) { // 添加内容到 <textarea> 同时记录标记信息
        const existingContent = textarea.value; // 获取 <textarea> 中已有的内容
        const newLine = existingContent ? '\n' : ''; // 判断是否已有内容,如果有则另起一行
        textarea.value += newLine + '\n' + `引用 [${paragraphId}]:${content} \n\n`; // 使用中括号来标记唯一标识符,并插入内容及空行
    }

    comments.forEach((p) => { // 遍历所有的 <comments> 元素
        p.addEventListener('click', () => { // 添加点击事件处理程序
            const targetDataId = p.textContent.match(/\[(.*?)\]/)[1]; // 只保留方括号里的 id 编码
            const targetParagraph = container.querySelector(`#${targetDataId}`); // 查找文章内容中对应的 id
            if (targetParagraph) {
                targetParagraph.scrollIntoView({ behavior: 'smooth' }); // 滚动到文章内容对应 id 的位置
            }
        });
    });

    </script>

至此,已经可以通过点击文章段落点评该段落内容(或图片)了。

感谢观赏

支持作者:Buy Me a 冰棒

Store

1 条评论

  1. 范德咪 1 noreply

    🐂!希望做成插件第一时间购买

准备下山 准备下山
Snapic Plus v5
使用 Lopwon POP 实现