普通视图

发现新文章,点击刷新页面。
昨天以前懋和道人
  • ✇懋和道人
  • Z-BlogPHP拓源纯净主题添谷歌、必应搜索记
    尝闻 Z-BlogPHP 之拓源纯净主题,简约精巧,然搜索之能,仅囿于站内。为扩其搜索之域,增谷歌、必应之搜索功能,以利访客便捷求知,遂着手于主题之 navbar.php 文件,行修改之事。于 navbar.php 中,寻得搜索表单代码之处。修改代码如下:{if $zbp->Config('tpure')->PostSEARCHON=='1'}     <div class="schico statefixed">         <a href="javascript:;"></a>         <div class="schfixed">             <form method="post" name="search" action="#" onsubmit="return handleSearch(this);" target="_blank">                 <input type="text" name="q" placeholder="{if $zbp->Co
     

Z-BlogPHP拓源纯净主题添谷歌、必应搜索记

2025年4月20日 08:20

尝闻 Z-BlogPHP 之拓源纯净主题,简约精巧,然搜索之能,仅囿于站内。为扩其搜索之域,增谷歌、必应之搜索功能,以利访客便捷求知,遂着手于主题之 navbar.php 文件,行修改之事。

于 navbar.php 中,寻得搜索表单代码之处。修改代码如下:

{if $zbp->Config('tpure')->PostSEARCHON=='1'}
    <div class="schico statefixed">
        <a href="javascript:;"></a>
        <div class="schfixed">
            <form method="post" name="search" action="#" onsubmit="return handleSearch(this);" target="_blank">
                <input type="text" name="q" placeholder="{if $zbp->Config('tpure')->PostSCHTXT}{$zbp->Config('tpure')->PostSCHTXT}{else}{$lang['tpure']['schtxt']}{/if}" autocomplete="off" class="schinput">
                <select name="engine">
                    <option value="site">站内搜索</option>
                    <option value="google">谷歌搜索</option>
                    <option value="bing">必应搜索</option>
                </select>
                <button type="submit" class="btn"></button>
            </form>
        </div>
    </div>
{/if}
{if $zbp->Config('tpure')->PostSEARCHON=='1'}
    <form method="post" name="search" action="#" onsubmit="return handleSearch(this);" class="sch-m" target="_blank">
        <input type="text" name="q" placeholder="{if $zbp->Config('tpure')->PostSCHTXT}{$zbp->Config('tpure')->PostSCHTXT}{else}{$lang['tpure']['schtxt']}{/if}" autocomplete="off" class="schinput">
        <select name="engine">
            <option value="site">站内搜索</option>
            <option value="google">谷歌搜索</option>
            <option value="bing">必应搜索</option>
        </select>
        <button type="submit" class="btn"></button>
    </form>
{/if}

此表单含输入框,以供输入搜索词;下拉选框,可择站内、谷歌、必应搜索;并有提交按钮。且设onsubmit事件,关联handleSearch函数,以理搜索逻辑。

复添JavaScript代码,以完搜索功能及样式调整:

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const searchForms = document.querySelectorAll('form[name="search"]');
        searchForms.forEach(form => {
            const input = form.querySelector('.schinput');
            const select = form.querySelector('select');
            const button = form.querySelector('.btn');
            input.style.position ='relative';
            input.style.paddingRight = '100px';
            input.style.boxSizing = 'border-box';
            select.style.position = 'absolute';
            select.style.left = '55%';
            select.style.top = '0';
            select.style.bottom = '0';
            select.style.border = 'none';
            select.style.background = 'transparent';
            select.style.appearance = 'none';
            select.style.webkitAppearance = 'none';
            select.style.mozAppearance = 'none';
            select.style.padding = '0 5px';
            button.style.position = 'absolute';
            if (window.innerWidth < 960) {
                button.style.top = 'auto';
            } else {
                button.style.top = '0';
                button.style.bottom = '0';
            }
            const inputParent = input.parentElement;
            inputParent.insertBefore(select, input.nextSibling);
            inputParent.insertBefore(button, select.nextSibling);
        });
    });

    function handleSearch(form) {
        const query = form.q.value;
        const engine = form.engine.value;
        const domain = "{$host}".replace(/^https?:\/\//, '').replace(/\/$/, '');

        if (engine ==='site') {
            form.action = "{$host}zb_system/cmd.php?act=search";
            return true;
        }

        let fullQuery;
        let url;

        switch (engine) {
            case 'google':
                fullQuery = "site:" + domain + " " + query;
                url = "https://www.google.com/search?q=" + encodeURIComponent(fullQuery);
                break;
            case 'bing':
                fullQuery = "site:" + domain + " " + query;
                url = "https://www.bing.com/search?q=" + encodeURIComponent(fullQuery);
                break;
        }

        window.open(url, '_blank');
        return false;
    }
</script>

此JavaScript代码,待页面DOM加载完毕,即遍历搜索表单,为输入框、下拉框、按钮添样式。依屏幕宽度,若小于960像素,则按钮top样式设为auto;否则,设top与bottom为0。并将下拉框与按钮嵌入输入框父元素,整其顺序。

handleSearch 函数取用户输入词与所选引擎。若为站内搜索,依原逻辑,设表单action为站内搜索地址;若为谷歌或必应搜索,则构含域名与搜索词之链接,于新窗口打开搜索结果,阻表单默认提交。

经此番修改,Z-BlogPHP拓源纯净主题搜索功能得以拓展,访客可按需选搜索引擎,或站内细索,或借谷歌、必应广求,便捷高效,望能为博客增色,予用户佳体验。


  • ✇懋和道人
  • 纪念自己哔站粉丝破八十大关
    《叹哔站》华阳洞侍香道子懋和道人乙巳莺时荧屏暗涌起妖氛,邪雾迷津祸万民。商贾逐膻抛典训,魑魅舞爪乱彝伦。本应雅乐传青史,却纵浮声惑赤心。谁守灵台澄澈处,玄门怅望泪沾襟。贫道幸寓哔哩之境,欣得八十知音垂顾。虽未奏黄钟大吕之雅韵,未应千岩万壑之共鸣,然于这嚣尘迷目、物欲障心之浊世,犹孤桐挺秀,栖凤来仪,聊慰玄门守正之忱。抬眸四瞩,但见乱象纷纭,恍若溟海翻澜,惊涛裂岸;恰似玄云蔽日,阴霾弥天。遂敛衽端容,援笔濡墨,欲效漆园遗风,以微言阐大义,借幽辞绘世情。观夫网络荧屏,众生如扑焰飞蛾,逐虚妄之华光;似衔泥蝼蚁,营浮靡之蜗角。或为蝇头虚名,构衅生隙,唇枪舌剑,聒噪若夏蝉沸树;或溺蜃楼幻梦,醉心绮靡,神昏志荡,颠狂如秋蝶迷丛。究其本源,非性本顽劣,实乃蒙昧于童蒙之教,熏染于浇薄之俗。譬若弱植投于硗埆,纵有凌云之志,难汲膏腴;恰似孤舟泛于溟渤,虽怀渡海之愿,易失津涯。习染所凝,志气所铄,诚不欺也!长此以往,恐堕迷途而罔觉,离 “致虚守静” 之真境,若背道而驰,愈行愈远。至若艺文之域,丹青失自然之韵,雕镂乖造化之工。虽笔走龙蛇,不过匠气满纸;纵墨染烟霞,实乃浮艳盈篇。辞藻则堆砌骈俪,如锦绣蒙尘;意象
     

纪念自己哔站粉丝破八十大关

2025年4月19日 00:17

《叹哔站》华阳洞侍香道子懋和道人乙巳莺时

荧屏暗涌起妖氛,邪雾迷津祸万民。

商贾逐膻抛典训,魑魅舞爪乱彝伦。

本应雅乐传青史,却纵浮声惑赤心。

谁守灵台澄澈处,玄门怅望泪沾襟。

image.png

贫道幸寓哔哩之境,欣得八十知音垂顾。虽未奏黄钟大吕之雅韵,未应千岩万壑之共鸣,然于这嚣尘迷目、物欲障心之浊世,犹孤桐挺秀,栖凤来仪,聊慰玄门守正之忱。抬眸四瞩,但见乱象纷纭,恍若溟海翻澜,惊涛裂岸;恰似玄云蔽日,阴霾弥天。遂敛衽端容,援笔濡墨,欲效漆园遗风,以微言阐大义,借幽辞绘世情。

观夫网络荧屏,众生如扑焰飞蛾,逐虚妄之华光;似衔泥蝼蚁,营浮靡之蜗角。或为蝇头虚名,构衅生隙,唇枪舌剑,聒噪若夏蝉沸树;或溺蜃楼幻梦,醉心绮靡,神昏志荡,颠狂如秋蝶迷丛。究其本源,非性本顽劣,实乃蒙昧于童蒙之教,熏染于浇薄之俗。譬若弱植投于硗埆,纵有凌云之志,难汲膏腴;恰似孤舟泛于溟渤,虽怀渡海之愿,易失津涯。习染所凝,志气所铄,诚不欺也!长此以往,恐堕迷途而罔觉,离 “致虚守静” 之真境,若背道而驰,愈行愈远。

image.png

至若艺文之域,丹青失自然之韵,雕镂乖造化之工。虽笔走龙蛇,不过匠气满纸;纵墨染烟霞,实乃浮艳盈篇。辞藻则堆砌骈俪,如锦绣蒙尘;意象则矫揉造作,若沐猴而冠。恰似巧言如簧,悦耳而惑心;画蛇添足,逞技而失真。此等流弊,如蚁穴溃堤,渐蚀文心;似鸩酒穿肠,暗伤雅道。致使观者昧 “大巧若拙” 之奥旨,失 “素处以默” 之本真。昔孟母三迁,择仁里以育贤;今时风颓,凭何术而挽狂澜?

论及阴阳之道,本应如太极流转,黑白交融;似日月递照,寒暑相推。然今多见断章取义之徒,标异矜奇;逞臆妄为之辈,炫异惑众。致使清浊淆乱,若泾渭合流;乾坤失序,如参商异位。如乾乾上下,似坤坤交合;譬犹断弦之瑟,虽响而不成律;溃堤之水,虽奔而失其道。探其渊薮,皆因教化不彰,德风未振,遂令大道蒙尘,世风日下。

贫道以玄微之语,发洪钟之响;借幽眇之辞,剖璞玉之精。虽言若阳春白雪,曲高和寡;义比玄霜绛雪,难觅知音。然犹效葛洪丹成罗浮,抱朴守真;慕陆修静经传玉笈,弘道阐教。今承先哲遗风,于网络布道,纵使荆棘塞途,险巇满径,亦当怀瑾握瑜,砥砺前行。冀望他日云销雨霁,朗月流辉,涤尽尘嚣之垢;风清日朗,惠风拂俗,重归淳和之境。使众生返璞归真,共沐大道之光;令网络复现清朗,同仰玄穹之净。

是以贫道愿竭己所能,于这纷扰网络,播撒正道之种,引众生远离迷障,共赴清明之途。 望诸公同襄盛举,携手并肩,以正风正气为舟楫,渡此网络迷津,共创一方澄澈乾坤。 庶几网络之中,邪佞退散,正道昭彰,化育群生,功莫大焉。 吾将秉持初心,以文字为针,以义理为线,精心编织网络空间的道德经纬,为营造和谐、文明、正气充盈的网络天地倾尽全力。 愿以此微薄之力,启网络之新程,令这虚拟天地,焕发出如仙境般的纯净与祥和。 虽前路漫漫,然吾心拳拳,矢志不渝,唯愿此愿得偿,不负所期。


  • ✇懋和道人
  • Z-BlogPHP 中自定义 Feed 输出功能的实现与应用
    在网站开发领域,为用户提供高效便捷的内容订阅途径,是提升用户体验、增强用户粘性的关键举措。在使用Z-BlogPHP搭建网站时,我们能够借助自定义PHP脚本,打造满足特定需求的Feed输出功能。接下来,本文将深入剖析feed_id.php和page_feed.php这两个脚本的功能、实现逻辑,以及基于它们所创建的 “订阅” 栏目。feed_id.php脚本主要用于依据Z-BlogPHP中的分类$categoryId,展示对应分类下的部分内容,生成分类的Feed输出。该脚本首先引入Z-BlogPHP系统核心文件并加载系统,为后续操作奠定基础。接着从URL参数获取分类ID,对其进行有效性验证,若分类ID无效或对应分类不存在,则输出错误信息并终止脚本运行。之后设置响应头为XML格式,生成XML声明及RSS 2.0文档的相关结构,并填充频道的标题(含分类名称和博客名称)、链接(分类链接)、描述(分类简介)和语言等信息。最后通过 $zbp->GetArticleList() 方法获取指定分类下正常发布的文章列表,按发布时间倒序排列,循环输出最多10篇文章的标题、链接、描述、发布日期和唯一标识
     

Z-BlogPHP 中自定义 Feed 输出功能的实现与应用

2025年4月2日 03:06

image.png

在网站开发领域,为用户提供高效便捷的内容订阅途径,是提升用户体验、增强用户粘性的关键举措。在使用Z-BlogPHP搭建网站时,我们能够借助自定义PHP脚本,打造满足特定需求的Feed输出功能。接下来,本文将深入剖析feed_id.php和page_feed.php这两个脚本的功能、实现逻辑,以及基于它们所创建的 “订阅” 栏目。

feed_id.php脚本主要用于依据Z-BlogPHP中的分类$categoryId,展示对应分类下的部分内容,生成分类的Feed输出。该脚本首先引入Z-BlogPHP系统核心文件并加载系统,为后续操作奠定基础。接着从URL参数获取分类ID,对其进行有效性验证,若分类ID无效或对应分类不存在,则输出错误信息并终止脚本运行。之后设置响应头为XML格式,生成XML声明及RSS 2.0文档的相关结构,并填充频道的标题(含分类名称和博客名称)、链接(分类链接)、描述(分类简介)和语言等信息。最后通过 $zbp->GetArticleList() 方法获取指定分类下正常发布的文章列表,按发布时间倒序排列,循环输出最多10篇文章的标题、链接、描述、发布日期和唯一标识符等内容,完成 Feed 的生成。比如,访问下方URL就能获取分类ID为2的分类内容的Feed输出。

https://www.dao.js.cn/feed_id.php?id=2

page_feed.php 脚本的核心功能是通过 GetPageList 方法获取网站内所有单页的信息,并生成相应的 Feed 输出。此脚本同样先引入并加载Z-BlogPHP系统,将当前操作类型设为feed并设置XML格式的响应头。然后执行相关钩子函数,生成XML声明和RSS 2.0文档的起始结构,填充频道的标题(含 “所有单页” 和博客名称)、链接(博客主页链接)、描述(所有单页的 RSS Feed)及语言等信息。再利用$zbp->GetPageList()方法获取所有单页列表,循环输出每个页面的标题、链接、描述(页面简介)、发布日期和唯一标识符等信息,最后结束XML结构并执行相应钩子函数。用户通过访问下方URL,便能获取所有的Feed输出。

https://www.dao.js.cn/page_feed.php

在完成feed_id.php和page_feed.php脚本的编写后,我们进一步创建了 “订阅” 栏目。用户访问 https://www.dao.js.cn/feed 即可进入该栏目,在此查看不同分类的内容Feed以及所有单页的Feed,及时掌握网站的最新动态。

通过对feed_id.php和page_feed.php脚本的精心开发与应用,我们在Z-BlogPHP网站中成功实现了自定义 Feed 输出功能,并搭建起 “订阅” 栏目。这不仅优化了网站的内容展示效果,还为用户提供了更为便捷的内容获取方式,有助于提升用户的留存率与活跃度。在今后的网站开发和维护中,我们还可依据实际需求,对这些脚本进行优化和拓展,以适应不断变化的业务场景。

需要feed_id.php和page_feed.php文件的,请通过“联系”栏目与我取得联系。feed页面模板如下:

    <h2>总体订阅</h2>                            
    <div class="category-wrapper">
        <a target="_blank" href="/feed.php" class="dingyue" title="南蛮子懋和博客默认订阅">
            <div class="dingyuetext">
                <div class="dingyuexl">默认订阅</div>
            </div>
            <i class="bi bi-rss"></i>
        </a>
        <a target="_blank" href="/page_feed.php" class="dingyue" title="南蛮子懋和博客单页订阅">
            <div class="dingyuetext">
                <div class="dingyuexl">单页订阅</div>
            </div>
            <i class="bi bi-rss"></i>
        </a>
    </div>
    <h2>分类订阅</h2>
    <div class="category-wrapper">
        {php}
        global $zbp;
        $categories = $zbp->GetCategoryList();
        foreach ($categories as $category) {
            if ($category->ID != 1) {
                $feed_url = 'feed_id.php?id='.$category->ID;
        {/php}
        <a target="_blank" href="{php}echo $feed_url;{/php}" class="dingyue" title="{php}echo $category->Intro;{/php}">
            <div class="dingyuetext">
                <div class="dingyuexl">{php}echo $category->Name;{/php}</div>
            </div>
            <i class="bi bi-rss"></i>
        </a>
        {php}
            }
        }
        {/php}
    </div>

依照Z-BlogPHP创建自定义页面方式,再合适的地方插入即可。需要feed样式的,可以自行f12复制,也可以向我索取,具体展示渲染效果如下:

https://www.dao.js.cn/feed

  • ✇懋和道人
  • 巧用张洪HeoMusic思路构建了我的“歌单”
    在音乐爱好者的世界里,拥有一个能自由听取的个性化歌单是许多人的梦想。为了实现这一目标,我借鉴了张洪 HeoMusic 的思路,成功搭建了属于自己的歌单页面。https://blog.zhheo.com/p/45699256.html张洪 HeoMusic 的思路为整个项目提供了关键的指引方向,其独特的架构理念和对音乐播放系统的理解,成为我搭建歌单页面的基石。在这个基础上,我对Meting.min.js进行了二次更改,这一举措极大地拓展了音乐获取的自由度。通过对代码的精细调整,我可以更灵活地从不同渠道获取心仪的音乐,让歌单内容更加丰富多样。值得注意的是因为跨域等问题,我们需要自己构建、或者反代一个API,因为版权原因,暂不提供思路,我是使用的injahow/meting-api使用cloudflare搭建的API。为了实现多id合并并打乱顺序输出json,我对Meting.min.js进行了下述修改(尊重著作权,console.log输出版权信息)class MetingJSElement extends HTMLElement {     constructor() {        
     

巧用张洪HeoMusic思路构建了我的“歌单”

2025年4月1日 02:51

image.png

在音乐爱好者的世界里,拥有一个能自由听取的个性化歌单是许多人的梦想。为了实现这一目标,我借鉴了张洪 HeoMusic 的思路,成功搭建了属于自己的歌单页面。

https://blog.zhheo.com/p/45699256.html

张洪 HeoMusic 的思路为整个项目提供了关键的指引方向,其独特的架构理念和对音乐播放系统的理解,成为我搭建歌单页面的基石。在这个基础上,我对Meting.min.js进行了二次更改,这一举措极大地拓展了音乐获取的自由度。通过对代码的精细调整,我可以更灵活地从不同渠道获取心仪的音乐,让歌单内容更加丰富多样。值得注意的是因为跨域等问题,我们需要自己构建、或者反代一个API,因为版权原因,暂不提供思路,我是使用的injahow/meting-api使用cloudflare搭建的API。

为了实现多id合并并打乱顺序输出json,我对Meting.min.js进行了下述修改(尊重著作权,console.log输出版权信息)

class MetingJSElement extends HTMLElement {
    constructor() {
        super();
        this._initialized = false;
    }

    connectedCallback() {
        if (window.APlayer && window.fetch &&!this._initialized) {
            this._init();
            this._parse();
            this._initialized = true;
        }
    }

    disconnectedCallback() {
        if (!this.lock && this.aplayer) {
            this.aplayer.destroy();
        }
    }

    _camelize(str) {
        return str
          .replace(/^[_.\- ]+/, '')
          .toLowerCase()
          .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase());
    }

    _init() {
        let config = {};
        for (let i = 0; i < this.attributes.length; i += 1) {
            config[this._camelize(this.attributes[i].name)] = this.attributes[i].value;
        }
        let keys = [
            'server', 'type', 'id', 'api', 'auth',
            'auto', 'lock',
            'name', 'title', 'artist', 'author', 'url', 'cover', 'pic', 'lyric', 'lrc',
        ];
        this.meta = {};
        for (let key of keys) {
            this.meta[key] = config[key];
            delete config[key];
        }
        this.config = config;

        this.api = this.meta.api || window.meting_api || 'https://xxx.xxx.xxx/?server=:server&type=:type&id=:id&r=:r';
        if (this.meta.auto) this._parse_link();

        // 支持多个 ID,将 id 属性按逗号分割成数组
        this.meta.ids = this.meta.id.split(',');
    }

    _parse_link() {
        let rules = [
            ['music.163.com.*song.*id=(\\d+)', 'netease', 'song'],
            ['music.163.com.*album.*id=(\\d+)', 'netease', 'album'],
            ['music.163.com.*artist.*id=(\\d+)', 'netease', 'artist'],
            ['music.163.com.*playlist.*id=(\\d+)', 'netease', 'playlist'],
            ['music.163.com.*discover/toplist.*id=(\\d+)', 'netease', 'playlist'],
            ['y.qq.com.*song/(\\w+).html', 'tencent', 'song'],
            ['y.qq.com.*album/(\\w+).html', 'tencent', 'album'],
            ['y.qq.com.*singer/(\\w+).html', 'tencent', 'artist'],
            ['y.qq.com.*playsquare/(\\w+).html', 'tencent', 'playlist'],
            ['y.qq.com.*playlist/(\\w+).html', 'tencent', 'playlist'],
            ['xiami.com.*song/(\\w+)', 'xiami', 'song'],
            ['xiami.com.*album/(\\w+)', 'xiami', 'album'],
            ['xiami.com.*artist/(\\w+)', 'xiami', 'artist'],
            ['xiami.com.*collect/(\\w+)', 'xiami', 'playlist'],
        ];

        for (let rule of rules) {
            let patt = new RegExp(rule[0]);
            let res = patt.exec(this.meta.auto);
            if (res!== null) {
                this.meta.server = rule[1];
                this.meta.type = rule[2];
                this.meta.id = res[1];
                return;
            }
        }
    }

    async _parse() {
        if (this.meta.url) {
            let result = {
                name: this.meta.name || this.meta.title || 'Audio name',
                artist: this.meta.artist || this.meta.author || 'Audio artist',
                url: this.meta.url,
                cover: this.meta.cover || this.meta.pic,
                lrc: this.meta.lrc || this.meta.lyric || '',
                type: this.meta.type || 'auto',
            };
            if (!result.lrc) {
                this.meta.lrcType = 0;
            }
            if (this.innerText) {
                result.lrc = this.innerText;
                this.meta.lrcType = 2;
            }
            this._loadPlayer([result]);
            return;
        }

        const allMusicData = [];
        for (const id of this.meta.ids) {
            let url = this.api
              .replace(':server', this.meta.server)
              .replace(':type', this.meta.type)
              .replace(':id', id)
              .replace(':auth', this.meta.auth)
              .replace(':r', Math.random());

            try {
                const res = await fetch(url);
                if (!res.ok) {
                    throw new Error(`HTTP error! status: ${res.status}`);
                }
                const text = await res.text();
                const result = JSON.parse(text);
                if (Array.isArray(result)) {
                    allMusicData.push(...result);
                } else {
                    console.error(`Data for ID ${id} is not an array:`, result);
                }
            } catch (error) {
                console.error(`Fetch error for ID ${id}:`, error);
            }
        }

        // 对获取到的所有音乐数据进行随机排序
        const shuffledMusicData = this._shuffleArray(allMusicData);

        this._loadPlayer(shuffledMusicData);
    }

    _shuffleArray(array) {
        for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
        }
        return array;
    }

    _loadPlayer(data) {
        let defaultOption = {
            audio: data,
            mutex: true,
            lrcType: this.meta.lrcType || 3,
            storageName: 'metingjs'
        };

        if (!data.length) return;

        let options = {
            ...defaultOption,
            ...this.config,
        };
        for (let optkey in options) {
            if (options[optkey] === 'true' || options[optkey] === 'false') {
                options[optkey] = (options[optkey] === 'true');
            }
        }

        let div = document.createElement('div');
        options.container = div;
        this.appendChild(div);

        this.aplayer = new APlayer(options);
    }
}

console.log('\n %c MetingJS v2.0.1 %c https://github.com/metowolf/MetingJS \n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');

if (window.customElements &&!window.customElements.get('meting-js')) {
    window.MetingJSElement = MetingJSElement;
    window.customElements.define('meting-js', MetingJSElement);
}

然而,在享受技术带来的便利时,我们绝不能忽视著作权的重要性。每一首音乐都是创作者的心血结晶,我们应当在合法合规的前提下使用这些作品,确保对知识产权的尊重。

在前端渲染方面,我选择了APlayer播放器。APlayer以其简洁美观的界面和强大的功能,为用户带来了优质的播放体验。它能够流畅地展示歌曲信息、控制播放进度,并且支持多种主题自定义,使歌单页面在功能性和美观性上达到了较好的平衡。

在前端仅需要随便往哪里插入下列代码即可(我是获取的“我收藏的歌单”下列的歌单,id是什么,需要自行理解,我尊重各站版权)

<link rel="stylesheet" href="/sucai/APlayer/APlayer.min.css">
<script src="/sucai/APlayer/APlayer.min.js"></script>
<script src="/sucai/APlayer/Meting.min.js"></script>
<meting-js server="netease" type="playlist" id="6948853317,326934130,446132364,978446985,2286543721,7235859079" autoplay="true" order="list" preload="auto" list-max-height="100vh"></meting-js>

通过这次实践,我不仅实现了歌单听取自由化的目标,还在技术运用和版权意识上有了更深的体会。希望我的经验能为其他音乐爱好者和开发者提供一些参考,共同打造更加优质、合法的音乐环境。

https://www.dao.js.cn/music

  • ✇懋和道人
  • 关于本站外链处理的思考与行动
    一、缘起在日常维护网站的过程中,一个细节问题引起了我的注意。一直以来,我都十分珍视与其他站点的友好合作关系,通过友情链接、联盟推广等方式,努力为用户拓展更多优质的信息渠道。然而,近期我在对网站进行深入检查时发现,博客中许多站外跳转链接,包括友链等,都没有正确设置 rel 标签。其实,由于一些不可控因素,之前我为所有站外跳转链接设置了中间跳转机制,本意是为了更好地管理和保障用户的浏览安全。但在这个过程中,却疏忽了 rel 标签的设置。https://www.dao.js.cn/new/2025030511627.shtml经过对友链的逐一排查,我发现目前共有 58 个友链站点。其中 41 个站点对出站链接(不限于友链)的 a 标签设置了 rel="noopener noreferrer nofollow",这体现了他们对网站 SEO 优化和链接管理的重视。有 5 个站点采用了转译的 golink 跳转方式处理出站链接,而 “爱写书” 站点则完全没有设置 target 和 rel 标签。其余站点虽设置了 target 跳转,但 rel 标签方面存在缺失。对于友情链接设置 rel 标签,我内
     

关于本站外链处理的思考与行动

2025年3月31日 01:49

一、缘起

在日常维护网站的过程中,一个细节问题引起了我的注意。一直以来,我都十分珍视与其他站点的友好合作关系,通过友情链接、联盟推广等方式,努力为用户拓展更多优质的信息渠道。然而,近期我在对网站进行深入检查时发现,博客中许多站外跳转链接,包括友链等,都没有正确设置 rel 标签。其实,由于一些不可控因素,之前我为所有站外跳转链接设置了中间跳转机制,本意是为了更好地管理和保障用户的浏览安全。但在这个过程中,却疏忽了 rel 标签的设置。

https://www.dao.js.cn/new/2025030511627.shtml

经过对友链的逐一排查,我发现目前共有 58 个友链站点。其中 41 个站点对出站链接(不限于友链)的 a 标签设置了 rel="noopener noreferrer nofollow",这体现了他们对网站 SEO 优化和链接管理的重视。有 5 个站点采用了转译的 golink 跳转方式处理出站链接,而 “爱写书” 站点则完全没有设置 target 和 rel 标签。其余站点虽设置了 target 跳转,但 rel 标签方面存在缺失。

对于友情链接设置 rel 标签,我内心一直有所顾虑。毕竟友情链接是网站之间友好交流的桥梁,设置 rel="noopener noreferrer nofollow" 可能会在一定程度上影响到友链站点的权益,感觉对友链朋友不够友好。尤其是 “爱写书” 站点,没有对我的站点链接做过多限制,这种信任让我很是感激。但从网站自身的 SEO 角度出发,缺失 rel 标签可能会导致 SEO 权重外流,影响网站在搜索引擎中的表现。

在这种纠结的心态下,我经过反复思考,最终决定还是要对网站的外链进行合理处理,以平衡网站自身发展和与友站的关系。

二、思考

从 SEO 的专业知识来讲,rel 标签的正确设置对于网站的权重分配和安全性至关重要。rel="noopener noreferrer nofollow" 这组属性,一方面可以有效防止搜索引擎将本站权重过多传递到外部链接,保障自身网站在搜索排名中的优势;另一方面,也能避免用户因误点恶意外链而带来的潜在风险。

然而,在实际操作中,要对全站的外链进行统一处理并非易事。网站的外链数量众多,来源复杂,涵盖了友链、广告链接、合作推广链接等多种类型。不同类型的外链,其功能和目的各异,处理方式也不能一概而论。而且,在处理外链时,还必须充分考虑用户体验。如果处理不当,可能会导致部分链接无法正常跳转,或者给用户的浏览过程带来不必要的困扰。经过慎重考虑,我认为对全站非 window.location.hostname 的 a 标签 url 施行强制 rel="noopener noreferrer nofollow" 处理是较为合适的方案。

这样既能最大程度地保障网站的 SEO 效果,又不会对用户的正常浏览造成严重影响。对于站内链接,用户可以自由访问;而对于站外链接,设置 rel 标签则是在保护网站自身利益的同时,也提醒用户谨慎对待外部链接。

三、实现

为了实现对全站外链的自动化处理,我借助了 JavaScript 技术。以下是具体的代码实现:

document.addEventListener('DOMContentLoaded', function () {
    const currentDomain = window.location.hostname;

    // 处理所有现有的 <a> 标签
    const allLinks = document.getElementsByTagName('a');
    for (let i = 0; i < allLinks.length; i++) {
        const link = allLinks[i];
        const href = link.href.trim();
        if (isValidExternalLink(href, currentDomain)) {
            link.setAttribute('rel', 'noopener noreferrer nofollow');
        }
    }

    // 创建一个 MutationObserver 实例来监听 DOM 变化
    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList') {
                // 遍历新增的节点
                for (const addedNode of mutation.addedNodes) {
                    if (addedNode.nodeName === 'A') {
                        const link = addedNode;
                        const href = link.href.trim();
                        if (isValidExternalLink(href, currentDomain)) {
                            link.setAttribute('rel', 'noopener noreferrer nofollow');
                        }
                    } else if (addedNode.querySelectorAll) {
                        // 检查新增节点的子节点中是否有 <a> 标签
                        const newLinks = addedNode.querySelectorAll('a');
                        for (const newLink of newLinks) {
                            const href = newLink.href.trim();
                            if (isValidExternalLink(href, currentDomain)) {
                                newLink.setAttribute('rel', 'noopener noreferrer nofollow');
                            }
                        }
                    }
                }
            } else if (mutation.type === 'attributes' && mutation.attributeName === 'href') {
                const link = mutation.target;
                const href = link.href.trim();
                if (isValidExternalLink(href, currentDomain)) {
                    link.setAttribute('rel', 'noopener noreferrer nofollow');
                }
            }
        }
    });

    // 配置 MutationObserver 监听的选项
    const config = { childList: true, subtree: true, attributes: true, attributeFilter: ['href'] };
    // 开始监听 document.body 及其子节点的变化
    observer.observe(document.body, config);

    function isValidExternalLink(href, currentDomain) {
        if (!href || href.startsWith('#') || href.startsWith('javascript:')) {
            return false;
        }
        try {
            const url = new URL(href);
            return url.hostname!== currentDomain;
        } catch (error) {
            return false;
        }
    }
});

这段代码的逻辑是:当页面加载完成(DOMContentLoaded 事件触发)时,获取当前页面的域名 currentDomain。然后遍历页面上所有的 <a> 标签,获取每个标签的 href 属性并进行修剪。通过 isValidExternalLink 函数判断该链接是否为有效的外部链接,如果是,则为其添加 rel="noopener noreferrer nofollow" 属性。同时,利用 MutationObserver 监听 DOM 的变化。当检测到有新节点添加(childList 类型变化)或 <a> 标签的 href 属性改变(attributes 类型变化且 attributeName 为 href)时,对新的或属性变化的 <a> 标签执行相同的检查和处理操作。

在代码编写完成后,我进行了严格的测试。先在本地开发环境中模拟各种页面情况和外链类型,确保代码能准确识别和处理外部链接。随后在生产环境中选取部分页面进行试点,观察实际运行效果并及时调整,最终保证代码能够稳定、有效地运行。

四、效果

image.png

经过对全站外链的处理,取得了较为显著的效果。从 SEO 方面来看,正确设置 rel 标签后,有效避免了 SEO 权重外流的问题。之前由于很多外链未设置 rel 标签,搜索引擎在抓取页面时可能会将部分权重传递到外部,影响网站自身排名。现在,搜索引擎能够更准确地处理外链,将权重更多地集中在站内页面,有助于提升网站在搜索结果中的整体排名。

在用户体验方面,虽然对外部链接设置了 rel 标签,但由于处理方式合理,并未对用户的正常浏览和跳转造成明显阻碍。用户依然可以自由点击外部链接,同时也在一定程度上提高了浏览的安全性,减少了恶意链接带来的潜在风险。此外,通过这次对外链的全面梳理和处理,我对网站的链接结构有了更清晰的认识。发现并清理了一些无效链接和重复链接,进一步优化了网站的性能和稳定性,为用户提供了更优质的浏览体验。

当然,SEO 优化是一个持续的过程,外链处理只是其中的一部分。未来,我会继续关注网站的 SEO 效果,不断优化和调整策略。同时,也会更加注重与友链站点的沟通与合作,在保障自身网站发展的前提下,维护好与友站的友好关系,共同促进互联网生态的健康发展。

思路来源于

https://blog.hslzz.cn/20250305191500.html

❌
❌