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

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

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

使用 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

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

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

  • 思源笔记

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

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

    19811 引用 • 75858 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 招聘

    哪里都缺人,哪里都不缺人。

    189 引用 • 1056 回帖 • 1 关注
  • 正则表达式

    正则表达式(Regular Expression)使用单个字符串来描述、匹配一系列遵循某个句法规则的字符串。

    31 引用 • 94 回帖
  • 笔记

    好记性不如烂笔头。

    306 引用 • 782 回帖
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    51 引用 • 37 回帖 • 2 关注
  • 博客

    记录并分享人生的经历。

    272 引用 • 2386 回帖
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 531 关注
  • 链滴

    链滴是一个记录生活的地方。

    记录生活,连接点滴

    141 引用 • 3721 回帖 • 1 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 141 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 233 关注
  • Telegram

    Telegram 是一个非盈利性、基于云端的即时消息服务。它提供了支持各大操作系统平台的开源的客户端,也提供了很多强大的 APIs 给开发者创建自己的客户端和机器人。

    5 引用 • 35 回帖 • 1 关注
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 19 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 232 回帖
  • HBase

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

    17 引用 • 6 回帖 • 58 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 234 关注
  • webpack

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

    41 引用 • 130 回帖 • 288 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 708 关注
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 31 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    60 引用 • 287 回帖
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 53 关注
  • 酷鸟浏览器

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

    3 引用 • 59 回帖 • 16 关注
  • MySQL

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

    675 引用 • 535 回帖
  • Kotlin

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

    19 引用 • 33 回帖 • 43 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 11 关注
  • API

    应用程序编程接口(Application Programming Interface)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

    76 引用 • 429 回帖
  • 书籍

    宋真宗赵恒曾经说过:“书中自有黄金屋,书中自有颜如玉。”

    76 引用 • 390 回帖
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 446 关注