通过 hack 的方式实现卡片式超链接

本贴最后更新于 600 天前,其中的信息可能已经时移世易

本来不是很想把这种比较蛋疼的实现方式贴出来的,但是看到好像很多人需要这个,就简单说一下实现的思路吧

使用 shadowdom 是为了防止 hack 的链接影响编辑器运行。

import { DOM监听器 } from "/script/public/DOMwatcher.js";
//DOM监听器只是一个observer的封装
export class linkCard extends naive.plugin {
  constructor() {
    super({ name: "linkCard" });
    window.requestAnimationFrame(() => this.hackLink());
    this.icons = naive.fs.readdirSync(
      naive.pathConstructor.naivePath() +
        "/script/plugin/corePlugins/linkCard/icon4Tsundoku"
    );
    window.siyuan.ws.ws.addEventListener("message", () => {
      this.hackLink();
    });
    document.addEventListener("mouseover", () => {
      this.hackLink();
    });
    let 监听选项1 = {
      监听目标: `[data-node-id]`,
//监听目标这里是指定了一个css选择器
      监听器回调: () => this.hackLink(),
//hacklink函数才是的功能代码
    };
    this.DOM监听器1 = new DOM监听器(监听选项1);
  }
  hackLink() {
    try{
    let links = document.querySelectorAll(
      '.protyle-wysiwyg.protyle-wysiwyg--attr [data-node-id] span[data-type="a"]'
    );
    links.forEach((link) => {
      if (link.dataset.title && link.dataset.title.indexOf("card:") >= 0) {
//意思是标题以card:开头的超链接才会显示成卡片形式
        if (!link.shadowRoot||!link.shadowRoot.innerHTML||!link.shadowRoot.querySelector("img")) {
//已经有shadowDOM的元素不重复添加
          this.attachLinkShadow(link);
        } else {
//已经有shadowDOM元素,修改之后需要确保图标和URL等正确
          let src = this.getImg(link.dataset.href);
          if (
            link.shadowRoot.querySelector("img").getAttribute("src") !== src
          ) {
            link.shadowRoot.querySelector("img").setAttribute("src", src);
   
          }
          if (link.dataset.title) {
            link.shadowRoot.querySelector(".LinkCard-title").innerText =
              link.dataset.title.slice(5);
          }
          if (link.dataset.href) {
              link.shadowRoot.querySelector(".LinkCard-href").innerText =
              link.dataset.href
            }

        }
      }
      else{
        link.shadowRoot?link.shadowRoot.innerHTML=`<span>${link.innerHTML}</span>`:null
      }
    });
  }catch(e){}
//出错时直接忽略不是好习惯,不过这里好像没有太大问题,因为触发频率比较高,如果在控制台输出错误信息很可能会卡死
  }
  attachLinkShadow(a) {
    a.shadowRoot?a.shadowRoot.innerHTML='':null
    //下面这段添加了卡片链接样式
    let linkStyle = document.createElement("style");
    linkStyle.innerHTML = `
    .LinkCard:hover{
        background-color:var(--b3-theme-primary-lightest) !important
    }
    .LinkCard{
    position: relative;
    display: -webkit-box;
    display: -webkit-flex;
    display: -ms-flexbox;
    display: flex;
    box-sizing: border-box;
    -webkit-flex-direction: row;
    -ms-flex-direction: row;
    flex-direction: row;
    -webkit-align-items: center;
    -webkit-box-align: center;
    -ms-flex-align: center;
    align-items: center;
    width: 60%;
    min-height: 84px;
    border-radius: 8px;
    max-width: 100%;
    overflow: hidden;
    margin: 16px auto;
    padding: 12px 12px 9px 12px;
    background-color: #F6F6F6;
    text-decoration:none 
  }
  .LinkCard-contents {
    display: block;
    -webkit-flex: 1 1 auto;
    -ms-flex: 1 1 auto;
    flex: 1 1 auto;
    position: relative;
    white-space:normal !important;
}
.LinkCard-image {
    -webkit-flex: 0 0 auto;
    -ms-flex: 0 0 auto;
    flex: 0 0 auto;
    background-color:var(--b3-theme-surface);
    background-size: cover;
    background-position: center;
    position: relative;
    display: block;
    width: 60px;
    height: 60px;
    margin-left: 20px;
    object-fit: cover;
    border-radius: inherit;
    overflow: hidden;
}
.LinkCard-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    top: 0;
    position: absolute;
}

.LinkCard-title {
    line-height: 20px;
    display: -webkit-box;
    text-overflow: ellipsis;
    overflow: hidden;
    color:var(--b3-theme-on-surface);
    -webkit-box-orient: vertical;
  
    -webkit-line-clamp: 2;

}
.LinkCard-href{
    line-height: 15px;
    font-size:15px;
    display: -webkit-box;
    text-overflow: ellipsis;
    overflow: hidden;
    color:var(--b3-theme-on-surface);
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 1;

}
  `;
    let shadow = a.shadowRoot?a.shadowRoot:a.attachShadow({ mode: "open" });

    shadow.innerHTML += `<div class="LinkCardContainer">
            <a class='LinkCard' href='${a.dataset.href}'>
                <span class='LinkCard-contents'>
                    <span class='LinkCard-title'>${a.dataset.title.slice(
                      5
                    )}</span>
                    <span class='LinkCard-href'>${a.dataset.href
                      }</span>
                </span>
                <span class="LinkCard-image">
                <img src="${this.getImg(a.dataset.href)}"></img>
            </span>

            </a>
        </div>`;
    shadow.appendChild(linkStyle);
  }
  getImg(href) {

//这里使用了icon4Tsundoku中的图标,故名思意,这些图标是从tsundoku主题偷来的....
//你也可以通过其他方式请求网站的图标,或者仅仅在远程请求失败的时候使用本地图标
    let iconNameRes =
      naive.pathConstructor.naivePath() +
      "/script/plugin/corePlugins/linkCard/icon4Tsundoku/" +
      "link2.svg";
    this.icons.forEach((iconName) => {
      if (iconName && iconName.split) {
        let iconURL = iconName.split(".")[0].replace("_", ".");
        if (href && href.indexOf(iconURL) >= 0) {
//这里的URL可能有有点怪,因为为了方便这里是跟naive另外拉起的服务器请求图标的,在其它地方实现的时候可以忽略
          iconNameRes =
            naive.pathConstructor.naivePath() +
            "/script/plugin/corePlugins/linkCard/icon4Tsundoku/" +
            iconName;
        }
      }
    });
    return iconNameRes;
  }
}
//这段代码可以忽略,因为这个本来是naive主题的一个插件,所以会有这个指定生效环境的内容,实际上不仅仅可以在APP环境生效,我这里是为了对比性能在其他环境关闭了这个插件
export const environments = ["APP"];

效果类似这样

image.png

想要其他样式就改一下 style 部分的代码。

image.png

貌似是不影响链接编辑的,就这样吧。

因为实现得比较随便,老哥们实际用的时候自己改改代码吧

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18591 引用 • 69190 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 522 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    330 引用 • 614 回帖 • 1 关注
  • OnlyOffice
    4 引用 • 27 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 44 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    675 引用 • 535 回帖
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    161 引用 • 473 回帖
  • 笔记

    好记性不如烂笔头。

    303 引用 • 777 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 619 关注
  • Hexo

    Hexo 是一款快速、简洁且高效的博客框架,使用 Node.js 编写。

    21 引用 • 140 回帖 • 24 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    163 引用 • 1450 回帖
  • 职场

    找到自己的位置,萌新烦恼少。

    126 引用 • 1699 回帖
  • 服务

    提供一个服务绝不仅仅是简单的把硬件和软件累加在一起,它包括了服务的可靠性、服务的标准化、以及对服务的监控、维护、技术支持等。

    41 引用 • 24 回帖 • 5 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    1 引用 • 11 回帖 • 1 关注
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 592 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 552 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 44 关注
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    41 引用 • 130 回帖 • 298 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • 996
    13 引用 • 200 回帖 • 1 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 25 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 22 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    18591 引用 • 69190 回帖 • 1 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    710 引用 • 1173 回帖 • 176 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖