浏览器插件-网址屏蔽器

隔壁李老太说自己孙子总是用电脑玩网页游戏,希望我帮忙做个浏览器插件,禁止他孙子玩电脑网页游戏。经过一番思想斗争,还是帮忙做了出来!

贴上源码,禁止商业用途,仅供学习参考交流使用,贴上源码如下:

background.js

// 插件状态
let isEnabled = true;

// 初始化
chrome.runtime.onInstalled.addListener(() => {
  // 设置默认状态为启用
  chrome.storage.local.set({ isEnabled: true });
  // 初始化空的拦截列表
  chrome.storage.local.set({ blockedSites: [] });
});

// 监听网页导航事件
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
  // 只处理主框架的导航
  if (details.frameId !== 0) return;
  
  // 检查插件是否启用
  chrome.storage.local.get(['isEnabled', 'blockedSites'], (result) => {
    if (!result.isEnabled) return;
    
    const url = new URL(details.url);
    const hostname = url.hostname;
    
    // 检查当前网址是否在拦截列表中
    const blockedSites = result.blockedSites || [];
    const isBlocked = blockedSites.some(site => {
      // 移除 http:// 或 https:// 前缀进行比较
      const siteHostname = site.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
      return hostname.includes(siteHostname) || siteHostname.includes(hostname);
    });
    
    if (isBlocked) {
      // 拦截访问,重定向到拦截页面
      chrome.tabs.update(details.tabId, {
        url: chrome.runtime.getURL('blocked.html')
      });
    }
  });
});

// 监听来自弹出窗口的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'toggleEnabled') {
    isEnabled = message.isEnabled;
    chrome.storage.local.set({ isEnabled });
    sendResponse({ success: true });
  }
  
  if (message.action === 'getState') {
    chrome.storage.local.get(['isEnabled', 'blockedSites'], (result) => {
      sendResponse({
        isEnabled: result.isEnabled,
        blockedSites: result.blockedSites || []
      });
    });
    return true; // 异步响应
  }
  
  if (message.action === 'addSite') {
    chrome.storage.local.get(['blockedSites'], (result) => {
      const blockedSites = result.blockedSites || [];
      // 检查是否已存在
      if (!blockedSites.includes(message.site)) {
        blockedSites.push(message.site);
        chrome.storage.local.set({ blockedSites });
      }
      sendResponse({ success: true, blockedSites });
    });
    return true; // 异步响应
  }
  
  if (message.action === 'removeSite') {
    chrome.storage.local.get(['blockedSites'], (result) => {
      let blockedSites = result.blockedSites || [];
      blockedSites = blockedSites.filter(site => site !== message.site);
      chrome.storage.local.set({ blockedSites });
      sendResponse({ success: true, blockedSites });
    });
    return true; // 异步响应
  }
});

blocked.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>网站已被拦截</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      text-align: center;
      padding: 50px;
      background-color: #f8f9fa;
    }
    .container {
      max-width: 600px;
      margin: 0 auto;
      background-color: white;
      padding: 30px;
      border-radius: 8px;
      box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
    }
    h1 {
      color: #e74c3c;
    }
    p {
      font-size: 18px;
      line-height: 1.6;
      color: #333;
    }
    .icon {
      font-size: 80px;
      color: #e74c3c;
      margin-bottom: 20px;
    }
    .button {
      display: inline-block;
      background-color: #3498db;
      color: white;
      padding: 10px 20px;
      border-radius: 4px;
      text-decoration: none;
      margin-top: 20px;
      transition: background-color 0.3s;
    }
    .button:hover {
      background-color: #2980b9;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="icon">⛔</div>
    <h1>网站已被拦截</h1>
    <p>您尝试访问的网站已被您设置为拦截。</p>
    <p>如果您想访问此网站,请在插件设置中移除该网站。</p>
    <a href="javascript:history.back()" class="button">返回上一页</a>
  </div>
</body>
</html>

manifest.json

{
  "manifest_version": 3,
  "name": "网站拦截工具",
  "version": "1.0",
  "description": "拦截指定的网站,防止访问",
  "permissions": ["storage", "webNavigation", "tabs"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "images/icon16.png",
      "48": "images/icon48.png",
      "128": "images/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  }
}

popup.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>网站拦截工具</title>
  <style>
    body {
      width: 350px;
      font-family: Arial, sans-serif;
      padding: 10px;
    }
    .header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
    }
    .toggle-container {
      display: flex;
      align-items: center;
    }
    .toggle {
      position: relative;
      display: inline-block;
      width: 50px;
      height: 24px;
      margin-left: 10px;
    }
    .toggle input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    .slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      transition: .4s;
      border-radius: 24px;
    }
    .slider:before {
      position: absolute;
      content: "";
      height: 16px;
      width: 16px;
      left: 4px;
      bottom: 4px;
      background-color: white;
      transition: .4s;
      border-radius: 50%;
    }
    input:checked + .slider {
      background-color: #2196F3;
    }
    input:checked + .slider:before {
      transform: translateX(26px);
    }
    .input-container {
      display: flex;
      margin-bottom: 15px;
    }
    #newSite {
      flex-grow: 1;
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      margin-right: 5px;
    }
    button {
      background-color: #4CAF50;
      color: white;
      border: none;
      padding: 8px 12px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 14px;
      border-radius: 4px;
      cursor: pointer;
    }
    button:hover {
      background-color: #45a049;
    }
    .search-container {
      margin-bottom: 15px;
    }
    #searchInput {
      width: 100%;
      padding: 8px;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
    }
    .site-list {
      max-height: 300px;
      overflow-y: auto;
      border: 1px solid #ddd;
      border-radius: 4px;
      margin-bottom: 15px;
    }
    .site-item {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 8px;
      border-bottom: 1px solid #eee;
    }
    .site-item:last-child {
      border-bottom: none;
    }
    .delete-btn {
      background-color: #f44336;
      padding: 5px 8px;
      font-size: 12px;
    }
    .delete-btn:hover {
      background-color: #d32f2f;
    }
    .pagination {
      display: flex;
      justify-content: center;
    }
    .pagination button {
      margin: 0 5px;
      background-color: #2196F3;
    }
    .pagination button:disabled {
      background-color: #cccccc;
      cursor: not-allowed;
    }
    .empty-message {
      text-align: center;
      padding: 20px;
      color: #666;
    }
  </style>
</head>
<body>
  <div class="header">
    <h2>网站拦截工具</h2>
    <div class="toggle-container">
      <span>启用</span>
      <label class="toggle">
        <input type="checkbox" id="enableToggle" checked>
        <span class="slider"></span>
      </label>
    </div>
  </div>
  
  <div class="input-container">
    <input type="text" id="newSite" placeholder="输入要拦截的网址 (例如: example.com)">
    <button id="addSite">添加</button>
  </div>
  
  <div class="search-container">
    <input type="text" id="searchInput" placeholder="搜索拦截的网站...">
  </div>
  
  <div class="site-list" id="siteList">
    <!-- 网站列表将在这里动态生成 -->
  </div>
  
  <div class="pagination" id="pagination">
    <!-- 分页按钮将在这里动态生成 -->
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

popup.js

document.addEventListener('DOMContentLoaded', function() {
  // 获取DOM元素
  const enableToggle = document.getElementById('enableToggle');
  const newSiteInput = document.getElementById('newSite');
  const addSiteButton = document.getElementById('addSite');
  const siteList = document.getElementById('siteList');
  const searchInput = document.getElementById('searchInput');
  const paginationDiv = document.getElementById('pagination');
  
  // 分页设置
  const itemsPerPage = 10;
  let currentPage = 1;
  let allSites = [];
  let filteredSites = [];
  
  // 初始化
  function init() {
    chrome.runtime.sendMessage({ action: 'getState' }, function(response) {
      enableToggle.checked = response.isEnabled;
      allSites = response.blockedSites || [];
      filteredSites = [...allSites];
      renderSiteList();
    });
  }
  
  // 切换插件启用状态
  enableToggle.addEventListener('change', function() {
    chrome.runtime.sendMessage({ 
      action: 'toggleEnabled', 
      isEnabled: enableToggle.checked 
    });
  });
  
  // 添加新网站
  addSiteButton.addEventListener('click', function() {
    const site = newSiteInput.value.trim();
    if (site) {
      chrome.runtime.sendMessage({ 
        action: 'addSite', 
        site: site 
      }, function(response) {
        if (response.success) {
          newSiteInput.value = '';
          allSites = response.blockedSites;
          filteredSites = [...allSites];
          searchInput.value = '';
          renderSiteList();
        }
      });
    }
  });
  
  // 回车键添加网站
  newSiteInput.addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
      addSiteButton.click();
    }
  });
  
  // 搜索功能
  searchInput.addEventListener('input', function() {
    const searchTerm = searchInput.value.toLowerCase();
    filteredSites = allSites.filter(site => 
      site.toLowerCase().includes(searchTerm)
    );
    currentPage = 1;
    renderSiteList();
  });
  
  // 渲染网站列表
  function renderSiteList() {
    siteList.innerHTML = '';
    
    if (filteredSites.length === 0) {
      siteList.innerHTML = '<div class="empty-message">没有拦截的网站</div>';
      paginationDiv.innerHTML = '';
      return;
    }
    
    // 计算分页
    const totalPages = Math.ceil(filteredSites.length / itemsPerPage);
    const startIndex = (currentPage - 1) * itemsPerPage;
    const endIndex = Math.min(startIndex + itemsPerPage, filteredSites.length);
    
    // 显示当前页的网站
    for (let i = startIndex; i < endIndex; i++) {
      const site = filteredSites[i];
      const siteItem = document.createElement('div');
      siteItem.className = 'site-item';
      
      const siteText = document.createElement('span');
      siteText.textContent = site;
      
      const deleteButton = document.createElement('button');
      deleteButton.className = 'delete-btn';
      deleteButton.textContent = '删除';
      deleteButton.addEventListener('click', function() {
        removeSite(site);
      });
      
      siteItem.appendChild(siteText);
      siteItem.appendChild(deleteButton);
      siteList.appendChild(siteItem);
    }
    
    // 渲染分页按钮
    renderPagination(totalPages);
  }
  
  // 渲染分页按钮
  function renderPagination(totalPages) {
    paginationDiv.innerHTML = '';
    
    if (totalPages <= 1) return;
    
    const prevButton = document.createElement('button');
    prevButton.textContent = '上一页';
    prevButton.disabled = currentPage === 1;
    prevButton.addEventListener('click', function() {
      if (currentPage > 1) {
        currentPage--;
        renderSiteList();
      }
    });
    
    const nextButton = document.createElement('button');
    nextButton.textContent = '下一页';
    nextButton.disabled = currentPage === totalPages;
    nextButton.addEventListener('click', function() {
      if (currentPage < totalPages) {
        currentPage++;
        renderSiteList();
      }
    });
    
    const pageInfo = document.createElement('span');
    pageInfo.textContent = `${currentPage}/${totalPages}`;
    pageInfo.style.margin = '0 10px';
    
    paginationDiv.appendChild(prevButton);
    paginationDiv.appendChild(pageInfo);
    paginationDiv.appendChild(nextButton);
  }
  
  // 删除网站
  function removeSite(site) {
    chrome.runtime.sendMessage({ 
      action: 'removeSite', 
      site: site 
    }, function(response) {
      if (response.success) {
        allSites = response.blockedSites;
        // 重新应用搜索过滤
        const searchTerm = searchInput.value.toLowerCase();
        filteredSites = allSites.filter(site => 
          site.toLowerCase().includes(searchTerm)
        );
        
        // 如果当前页没有内容了,回到上一页
        const totalPages = Math.ceil(filteredSites.length / itemsPerPage);
        if (currentPage > totalPages && currentPage > 1) {
          currentPage = totalPages;
        }
        
        renderSiteList();
      }
    });
  }
  
  // 初始化
  init();
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值