上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖
楼主: catscript1 - 

你的下一个返回顶部按钮,何必只是一个返回按钮?新人写脚本求关照

[复制链接]

该用户从未签到

1

主题

0

回帖

3

积分

助理工程师

积分
3
发表于 3 小时前 | 显示全部楼层 | 阅读模式
安装此脚本 如何安装? 脚本问题反馈 给脚本评分 查看代码

它可以是最纯粹的返回顶部按钮——单击平滑回顶、悬停展开快捷菜单,安静克制、绝不多事;也可以是你想要的任何东西:划词翻译、定时提醒、GitHub 加速下载,装上即用。想要的功能还没有?一句话的事儿——脚本天生支持 SKILL,任何 AI 读完开发指南就能替你写插件:你说想法,它出代码。

开发背景

某天逛某几个网站的时候发现没有返回到顶的功能,搜索了一下非常多的插件,那为什么还要做一个呢,大多资质平平在我看来🤣(主要就是好玩),所以准备抄刀做一个顺便体验体验开发脚本(白送的token不用白不用😝),一顿操作下来开发出来了这个在海量脚本的扩展上又提供了扩展能力的脚本(套娃了说是🤡)。

目前内置了三个扩展,支持给Github网站的链接增加快捷下载按钮(已镜像加速,可配置),快捷画词翻译(实在找不到好的免费API,默认用的是mymemory),提醒助手(喝水有多重要知道吗😎,懂得都懂)。这些是内置的(谁要你的内置啊~喂🫣,可以设置禁用,其实默认也没有启用,得麻烦你长按自己设置),当然你也可以增加你自己想要的功能通过开发插件随便折腾,实在不懂的也想体验自定义功能的可以把SKILL发给某AI老师分分钟给你整出来。

怎么体验,插件在哪里?

传送门:火箭按钮

以下是采访wor***ddy老师插件编写体验

image.png

附带一个blocker

// ==Plugin==
// @id          com.local.popup-blocker
// @name        公告弹窗拦截
// @version     2.3.1
// @type        feature
// @permission  storage
// @icon        data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Ctext y='0.9em' font-size='90'%3E%F0%9F%9B%A1%EF%B8%8F%3C/text%3E%3C/svg%3E
// @description 按拦截列表自动隐藏网页公告/广告弹窗,每轮扫描到命中都会提示(重复拦截也通知),并解除弹窗造成的页面滚动锁定
// ==/Plugin==
(function (ctx) {
  'use strict';

  var STYLE_ID = 'rocket-popup-blocker-style';
  var HIDE_FLAG = 'data-rocket-hidden';

  // 默认拦截列表:netflixgc.tv 的开站公告层(#notice / .ds-pop)及其「我已了解」按钮
  var DEFAULT_SELECTORS = '#notice, div.ds-pop, #ds_notice_ok';

  // 弹窗常见的「锁滚动」类名
  var LOCK_CLASSES = [
    'no-scroll',
    'modal-open',
    'pop-lock',
    'lock-scroll',
    'overflow-hidden',
    'stop-scroll',
  ];

  var observer = null;
  var timers = [];
  var hiddenNodes = [];
  var cachedSelectors = [];
  var blockedTimes = 0; // 拦截次数计数:让每次通知文案都不同,避免被通知层当成重复消息吞掉
  var lastNotifyAt = 0;

  // 两次通知之间的最小间隔(毫秒)。每次扫描到命中都会通知,这个间隔只用来防止
  // 同一瞬间的连续几轮扫描把通知刷爆;想更密集就调小,想更少打扰就调大。
  var MIN_NOTIFY_GAP = 1000;

  /* ---------- 读取拦截列表(逗号 / 换行 / 分号分隔,与网址无关,命中即拦) ---------- */

  function parseSelectors(raw, fallback) {
    var list = String(raw == null ? '' : raw)
      .split(/[\n,;]/)
      .map(function (s) { return s.trim(); })
      .filter(Boolean);
    if (!list.length) list = fallback;
    // 丢掉浏览器不认识的选择器,避免一条写错拖垮整条 CSS 规则
    return list.filter(function (sel) {
      try { document.querySelector(sel); return true; } catch (e) { return false; }
    });
  }

  function selectorList() {
    var raw = ctx.getValue('selectors', DEFAULT_SELECTORS);
    return parseSelectors(raw, parseSelectors(DEFAULT_SELECTORS, ['#notice']));
  }

  function selectorsChanged() {
    var next = selectorList();
    if (next.join(',') !== cachedSelectors.join(',')) {
      cachedSelectors = next;
      return true;
    }
    return false;
  }

  /* ---------- 注入兜底样式(列表变动时同步刷新) ---------- */

  function injectStyle() {
    var css = cachedSelectors.join(',');
    if (!css) return;
    var style = document.getElementById(STYLE_ID);
    if (!style) {
      style = document.createElement('style');
      style.id = STYLE_ID;
      (document.head || document.documentElement).appendChild(style);
    }
    var text =
      css + '{display:none !important;visibility:hidden !important;pointer-events:none !important;}';
    if (style.textContent !== text) style.textContent = text;
  }

  /* ---------- 解锁被弹窗锁住的页面滚动 ---------- */

  function unlockScroll() {
    var html = document.documentElement;
    var body = document.body;
    [html, body].forEach(function (el) {
      if (!el || !el.style) return;
      if (el.style.overflow === 'hidden' || el.style.overflowY === 'hidden') {
        el.style.overflow = '';
        el.style.overflowY = '';
      }
      // 移动端常见写法:body{position:fixed;top:-xxx}
      if (body && el === body && el.style.position === 'fixed') {
        el.style.position = '';
        el.style.top = '';
        el.style.width = '';
      }
    });
    if (body) {
      LOCK_CLASSES.forEach(function (c) { body.classList.remove(c); });
    }
  }

  /* ---------- 判断元素是否已被上层被拦元素包住 ---------- */

  function isNested(el) {
    var p = el.parentNode;
    while (p && p.getAttribute) {
      if (p.getAttribute(HIDE_FLAG) === '1') return true;
      p = p.parentNode;
    }
    return false;
  }

  /* ---------- 压住单个元素 ---------- */

  function hide(el) {
    if (el.getAttribute(HIDE_FLAG) !== '1') {
      el.setAttribute(HIDE_FLAG, '1');
      hiddenNodes.push(el); // 只登记一次,卸载时逐个还原
    }
    el.style.setProperty('display', 'none', 'important');
    el.style.setProperty('visibility', 'hidden', 'important');
    el.style.setProperty('pointer-events', 'none', 'important');
  }

  /* ---------- 每次拦截都通知(重复拦截也照发) ---------- */

  function notify(top, names) {
    var now = Date.now();
    if (lastNotifyAt && now - lastNotifyAt < MIN_NOTIFY_GAP) return; // 同一瞬间的连扫合并成一条
    lastNotifyAt = now;
    blockedTimes += 1;
    ctx.toast(
      '已拦截弹窗 ' + top + ' 个:' + names.join('、') + '(第 ' + blockedTimes + ' 次)',
      { type: 'success', timeout: 3000 }
    );
  }

  /* ---------- 扫描并隐藏 ---------- */

  function sweep() {
    if (selectorsChanged()) injectStyle();

    var hit = 0;
    var counted = [];
    var top = 0;
    var names = [];

    cachedSelectors.forEach(function (sel) {
      var nodes;
      try { nodes = document.querySelectorAll(sel); } catch (e) { return; }
      for (var i = 0; i < nodes.length; i++) {
        var el = nodes[i];

        // 同一个元素可能被列表里多条选择器同时命中,只算一次
        if (counted.indexOf(el) !== -1) continue;
        counted.push(el);

        var nested = isNested(el);

        // 不管元素是新出现的还是早就被压住的,每轮扫描都重新压一遍并计入本次拦截
        hide(el);
        hit++;

        if (!nested) {
          top++;
          if (names.indexOf(sel) === -1) names.push(sel);
        }
      }
    });

    if (hit > 0) {
      unlockScroll();
      if (top > 0) notify(top, names);
    }
    return hit;
  }

  /* ---------- 持续拦截动态插入的弹窗 ---------- */

  function watch() {
    if (!document.documentElement || typeof MutationObserver === 'undefined') return;
    observer = new MutationObserver(function (records) {
      for (var i = 0; i < records.length; i++) {
        if (records[i].addedNodes && records[i].addedNodes.length) {
          sweep();
          return;
        }
      }
    });
    observer.observe(document.documentElement, { childList: true, subtree: true });
  }

  /* ---------- 设置项:只暴露拦截列表 ---------- */

  ctx.declareSettings([
    {
      key: 'selectors',
      label: '拦截列表(选择器,逗号分隔,对任意网址生效)',
      type: 'text',
      placeholder: '#notice, .ds-pop, .my-popup',
      default: DEFAULT_SELECTORS,
    },
  ]);

  /* ---------- 启动 ---------- */

  selectorsChanged();
  injectStyle();
  sweep();
  watch();
  // 兜底:应对延迟渲染的公告层
  timers.push(setTimeout(sweep, 300));
  timers.push(setTimeout(sweep, 1200));
  timers.push(setTimeout(sweep, 3000));

  /* ---------- 卸载清理 ---------- */

  ctx.onDispose(function () {
    if (observer) { observer.disconnect(); observer = null; }
    timers.forEach(clearTimeout);
    timers = [];

    var style = document.getElementById(STYLE_ID);
    if (style && style.parentNode) style.parentNode.removeChild(style);

    hiddenNodes.forEach(function (el) {
      el.removeAttribute(HIDE_FLAG);
      el.style.removeProperty('display');
      el.style.removeProperty('visibility');
      el.style.removeProperty('pointer-events');
    });
    hiddenNodes = [];
  });
})(ctx);

不得不说,现在的ai是真的墙,人都麻了!!!

发表回复

本版积分规则