不可见字符生成器
<!DOCTYPE html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>不可见字符检测与生成工具</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/v4-shims.min.css">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#36D399',
warning: '#FB8C00',
danger: '#F87272',
dark: '#1E293B',
light: '#F8FAFC'
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.text-shadow {
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.bg-gradient-tech {
background: linear-gradient(135deg, #165DFF 0%, #36D399 100%);
}
.glass-effect {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.char-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 8px;
}
.char-card {
transition: all 0.3s ease;
}
.char-card:hover {
transform: translateY(-3px);
box-shadow: 0 10px 25px rgba(22, 93, 255, 0.15);
}
.highlighted-text {
white-space: pre-wrap;
word-break: break-all;
}
.invisible-char {
background: linear-gradient(45deg, #F87272, #FB8C00);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.85em;
margin: 0 2px;
animation: pulse-slow 3s infinite;
}
.result-card {
border-left: 4px solid #165DFF;
}
.warning-card {
border-left: 4px solid #FB8C00;
}
.danger-card {
border-left: 4px solid #F87272;
}
.feature-card {
transition: all 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 35px rgba(0,0,0,0.1);
}
.nav-link {
position: relative;
}
.nav-link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background: #165DFF;
transition: width 0.3s ease;
}
.nav-link:hover::after {
width: 100%;
}
.nav-link.active::after {
width: 100%;
}
</style><!-- Header -->
<header class="bg-white/90 backdrop-blur-md shadow-sm sticky top-0 z-50">
<div class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-tech rounded-lg flex items-center justify-center">
<i class="fa fa-eye text-white text-lg"></i>
</div>
<h1 class="text-xl font-bold text-dark">不可见字符工具</h1>
</div>
<nav class="hidden md:flex space-x-6">
<a href="#detector" class="nav-link text-dark hover:text-primary transition-colors">字符检测</a>
<a href="#generator" class="nav-link text-dark hover:text-primary transition-colors">字符生成</a>
<a href="#reference" class="nav-link text-dark hover:text-primary transition-colors">字符参考</a>
<a href="#security" class="nav-link text-dark hover:text-primary transition-colors">安全知识</a>
</nav>
<button class="md:hidden text-dark" id="mobileMenuBtn">
<i class="fa fa-bars text-xl"></i>
</button>
</div>
</div>
<!-- Mobile Menu -->
<div class="md:hidden bg-white shadow-lg absolute w-full left-0 top-full transform -translate-y-full opacity-0 transition-all duration-300 ease-in-out" id="mobileMenu">
<div class="container mx-auto px-4 py-3">
<nav class="flex flex-col space-y-3">
<a href="#detector" class="text-dark hover:text-primary py-2 transition-colors">字符检测</a>
<a href="#generator" class="text-dark hover:text-primary py-2 transition-colors">字符生成</a>
<a href="#reference" class="text-dark hover:text-primary py-2 transition-colors">字符参考</a>
<a href="#security" class="text-dark hover:text-primary py-2 transition-colors">安全知识</a>
</nav>
</div>
</div>
</header>
<!-- Hero Section -->
<section class="py-16 md:py-24 bg-gradient-tech text-white">
<div class="container mx-auto px-4 text-center">
<h2 class="text-[clamp(2rem,5vw,3.5rem)] font-bold mb-6 text-shadow animate-fade-in">
发现隐藏的字符世界
</h2>
<p class="text-[clamp(1rem,2vw,1.25rem)] mb-8 max-w-3xl mx-auto opacity-90 animate-slide-up">
检测、生成和分析那些看不见的Unicode字符,保护您的文本安全,了解字符的奥秘
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center animate-slide-up">
<a href="#detector" class="bg-white text-primary px-8 py-3 rounded-lg font-semibold hover:bg-opacity-90 transition-all transform hover:scale-105 shadow-lg">
<i class="fa fa-search mr-2"></i>开始检测
</a>
<a href="#generator" class="bg-transparent border-2 border-white text-white px-8 py-3 rounded-lg font-semibold hover:bg-white/10 transition-all">
<i class="fa fa-magic mr-2"></i>生成字符
</a>
</div>
</div>
</section>
<!-- Features Section -->
<section class="py-16 bg-white">
<div class="container mx-auto px-4">
<h3 class="text-3xl font-bold text-center mb-12 text-dark">强大功能</h3>
<div class="grid md:grid-cols-3 gap-8">
<div class="feature-card bg-gradient-to-br from-blue-50 to-blue-100 p-6 rounded-xl shadow-lg">
<div class="w-14 h-14 bg-primary rounded-full flex items-center justify-center mb-4">
<i class="fa fa-search text-white text-xl"></i>
</div>
<h4 class="text-xl font-semibold mb-3 text-dark">智能检测</h4>
<p class="text-gray-600">自动识别文本中的不可见字符,高亮显示并提供详细信息</p>
</div>
<div class="feature-card bg-gradient-to-br from-green-50 to-green-100 p-6 rounded-xl shadow-lg">
<div class="w-14 h-14 bg-secondary rounded-full flex items-center justify-center mb-4">
<i class="fa fa-magic text-white text-xl"></i>
</div>
<h4 class="text-xl font-semibold mb-3 text-dark">字符生成</h4>
<p class="text-gray-600">生成各种类型的不可见字符,用于测试和开发目的</p>
</div>
<div class="feature-card bg-gradient-to-br from-orange-50 to-orange-100 p-6 rounded-xl shadow-lg">
<div class="w-14 h-14 bg-warning rounded-full flex items-center justify-center mb-4">
<i class="fa fa-shield text-white text-xl"></i>
</div>
<h4 class="text-xl font-semibold mb-3 text-dark">安全防护</h4>
<p class="text-gray-600">了解不可见字符的安全风险,保护您的文本和数据</p>
</div>
</div>
</div>
</section>
<!-- Detector Section -->
<section id="detector" class="py-16 bg-slate-50">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h3 class="text-3xl font-bold mb-8 text-dark">字符检测器</h3>
<p class="text-gray-600 mb-8">粘贴您的文本,我们将检测其中的不可见字符并高亮显示</p>
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
<div class="mb-6">
<label class="block text-gray-700 font-medium mb-2">输入文本</label>
<textarea
id="inputText"
class="w-full h-48 p-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all resize-none"
placeholder="在此粘贴您的文本..."></textarea>
</div>
<div class="flex flex-col sm:flex-row gap-4">
<button
id="detectBtn"
class="bg-primary text-white px-6 py-3 rounded-lg font-semibold hover:bg-primary/90 transition-all flex items-center justify-center"
>
<i class="fa fa-search mr-2"></i>检测字符
</button>
<button
id="clearBtn"
class="bg-gray-200 text-gray-700 px-6 py-3 rounded-lg font-semibold hover:bg-gray-300 transition-all flex items-center justify-center"
>
<i class="fa fa-trash mr-2"></i>清空
</button>
<button
id="copyResultBtn"
class="bg-secondary text-white px-6 py-3 rounded-lg font-semibold hover:bg-secondary/90 transition-all flex items-center justify-center"
>
<i class="fa fa-copy mr-2"></i>复制结果
</button>
</div>
</div>
<!-- Results -->
<div id="resultsContainer" class="hidden">
<h4 class="text-xl font-semibold mb-4 text-dark">检测结果</h4>
<div class="bg-white rounded-xl shadow-lg p-6 mb-6">
<h5 class="text-lg font-medium mb-3 text-gray-700">可视化结果</h5>
<div id="visualResult" class="highlighted-text bg-gray-50 p-4 rounded-lg border border-gray-200 min-h-24"></div>
</div>
<div id="detailedResults" class="space-y-4"></div>
</div>
</div>
</div>
</section>
<!-- Generator Section -->
<section id="generator" class="py-16 bg-white">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h3 class="text-3xl font-bold mb-8 text-dark">字符生成器</h3>
<p class="text-gray-600 mb-8">选择您需要的不可见字符类型,我们将为您生成</p>
<div class="bg-gradient-to-br from-blue-50 to-blue-100 rounded-xl p-6 mb-8">
<div class="mb-6">
<h4 class="text-xl font-semibold mb-2 text-dark">真正不可见字符</h4>
<p class="text-gray-600 text-sm">这些字符在视觉上完全不可见,但会影响文本处理。点击卡片即可生成。</p>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer relative overflow-hidden" data-char-code="200B" data-name="零宽空格 (ZWSP)" data-code="U+200B" title="完全不可见,用于单词分隔">
<div class="absolute top-2 right-2 bg-primary/10 text-primary text-xs px-2 py-1 rounded-full">
最常用
</div>
<div class="text-center">
<div class="text-primary font-bold mb-2">ZWSP</div>
<div class="text-sm text-gray-600">零宽空格</div>
<div class="text-xs text-gray-500 mt-1">U+200B</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="200C" data-name="零宽非连接符 (ZWNJ)" data-code="U+200C" title="完全不可见,防止字符连接">
<div class="text-center">
<div class="text-primary font-bold mb-2">ZWNJ</div>
<div class="text-sm text-gray-600">零宽非连接符</div>
<div class="text-xs text-gray-500 mt-1">U+200C</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="200D" data-name="零宽连接符 (ZWJ)" data-code="U+200D" title="完全不可见,强制字符连接">
<div class="text-center">
<div class="text-primary font-bold mb-2">ZWJ</div>
<div class="text-sm text-gray-600">零宽连接符</div>
<div class="text-xs text-gray-500 mt-1">U+200D</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="FEFF" data-name="零宽无断空格 (BOM)" data-code="U+FEFF" title="完全不可见,字节顺序标记">
<div class="text-center">
<div class="text-primary font-bold mb-2">BOM</div>
<div class="text-sm text-gray-600">零宽无断空格</div>
<div class="text-xs text-gray-500 mt-1">U+FEFF</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="2060" data-name="零宽断行符 (WJ)" data-code="U+2060" title="完全不可见,防止换行">
<div class="text-center">
<div class="text-primary font-bold mb-2">WJ</div>
<div class="text-sm text-gray-600">零宽断行符</div>
<div class="text-xs text-gray-500 mt-1">U+2060</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="200E" data-name="左至右符 (LRM)" data-code="U+200E" title="完全不可见,控制文本方向">
<div class="text-center">
<div class="text-primary font-bold mb-2">LRM</div>
<div class="text-sm text-gray-600">左至右符</div>
<div class="text-xs text-gray-500 mt-1">U+200E</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="200F" data-name="右至左符 (RLM)" data-code="U+200F" title="完全不可见,控制文本方向">
<div class="text-center">
<div class="text-primary font-bold mb-2">RLM</div>
<div class="text-sm text-gray-600">右至左符</div>
<div class="text-xs text-gray-500 mt-1">U+200F</div>
</div>
</div>
<div class="char-card bg-white p-4 rounded-lg shadow hover:shadow-md cursor-pointer" data-char-code="2061" data-name="函数应用符" data-code="U+2061" title="完全不可见,数学符号">
<div class="text-center">
<div class="text-primary font-bold mb-2">U+2061</div>
<div class="text-sm text-gray-600">函数应用符</div>
<div class="text-xs text-gray-500 mt-1">数学字符</div>
</div>
</div>
</div>
<div class="mt-6 p-4 bg-white rounded-lg shadow">
<div class="flex items-start">
<div class="w-8 h-8 bg-primary/10 rounded-full flex items-center justify-center mr-3 mt-1">
<i class="fa fa-info text-primary"></i>
</div>
<div>
<h5 class="font-medium text-dark mb-1">关于不可见字符</h5>
<p class="text-gray-600 text-sm">这些字符在视觉上完全不可见,但会:<br>
• 影响字符串长度计算<br>
• 导致文本比较失败<br>
• 被用于隐写术和水印<br>
• 可能被恶意用于钓鱼攻击</p>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6">
<div class="mb-6">
<h4 class="text-xl font-semibold mb-2 text-dark">生成的不可见字符</h4>
<p class="text-gray-600 text-sm mb-4">
这些字符在视觉上完全不可见,但会被添加到文本框中。您可以复制它们并粘贴到其他地方。
<br>
<span class="text-warning font-medium">提示:</span>您可以在文本框中输入普通文字,然后插入不可见字符来测试效果。
</p>
<div class="relative">
<div class="absolute inset-0 bg-gradient-to-br from-primary/5 to-secondary/5 rounded-lg pointer-events-none"></div>
<textarea
id="generatedText"
class="w-full h-24 p-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-all resize-none relative z-10"
placeholder="点击上方字符卡片生成不可见字符..."></textarea>
<div id="charCountBadge" class="absolute top-2 right-2 bg-primary/10 text-primary text-xs px-2 py-1 rounded-full z-20">
字符数: 0
</div>
<div id="invisibleHint" class="absolute bottom-2 left-4 text-gray-500 text-xs z-20 hidden">
<i class="fa fa-eye-slash mr-1"></i>
<span>包含不可见字符</span>
</div>
</div>
</div>
<div class="flex gap-4">
<button
id="copyGeneratedBtn"
class="bg-secondary text-white px-6 py-3 rounded-lg font-semibold hover:bg-secondary/90 transition-all flex items-center justify-center"
>
<i class="fa fa-copy mr-2"></i>复制字符
</button>
<button
id="clearGeneratedBtn"
class="bg-gray-200 text-gray-700 px-6 py-3 rounded-lg font-semibold hover:bg-gray-300 transition-all flex items-center justify-center"
>
<i class="fa fa-trash mr-2"></i>清空
</button>
</div>
</div>
</div>
</div>
</section>
<!-- Reference Section -->
<section id="reference" class="py-16 bg-slate-50">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h3 class="text-3xl font-bold mb-8 text-dark">字符参考</h3>
<p class="text-gray-600 mb-8">了解各种不可见字符的详细信息和用途</p>
<div class="grid md:grid-cols-2 gap-6 mb-8">
<div class="bg-white rounded-xl shadow-lg p-6 result-card">
<h4 class="text-lg font-semibold mb-4 text-dark">零宽字符 (完全不可见)</h4>
<ul class="space-y-3 text-gray-600">
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+200B:</span>
<span>零宽空格 (ZWSP) - 用于单词分隔,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+200C:</span>
<span>零宽非连接符 (ZWNJ) - 防止字符连接,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+200D:</span>
<span>零宽连接符 (ZWJ) - 强制字符连接,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+FEFF:</span>
<span>零宽无断空格 (BOM) - 字节顺序标记,完全不可见</span>
</li>
</ul>
</div>
<div class="bg-white rounded-xl shadow-lg p-6 result-card">
<h4 class="text-lg font-semibold mb-4 text-dark">控制字符 (完全不可见)</h4>
<ul class="space-y-3 text-gray-600">
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+200E:</span>
<span>左至右符 (LRM) - 控制文本方向,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+200F:</span>
<span>右至左符 (RLM) - 控制文本方向,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+2060:</span>
<span>零宽断行符 (WJ) - 防止换行,完全不可见</span>
</li>
<li class="flex items-start">
<span class="text-primary font-semibold mr-2">U+2061-2064:</span>
<span>不可见数学符号 - 数学公式控制,完全不可见</span>
</li>
</ul>
</div>
</div>
<div class="bg-warning/10 rounded-xl p-6 mb-8">
<div class="flex items-start">
<div class="w-10 h-10 bg-warning rounded-full flex items-center justify-center mr-3">
<i class="fa fa-exclamation-triangle text-white"></i>
</div>
<div>
<h4 class="text-lg font-semibold mb-2 text-dark">重要提示</h4>
<p class="text-gray-700">
<strong>注意区分:</strong>有些字符(如U+00A0非断空格)虽然被称为"空格",但实际上是可见的,只会显示为普通空格但不会换行。本工具只处理<strong>完全不可见</strong>的字符。
</p>
</div>
</div>
</div>
<div class="bg-white rounded-xl shadow-lg p-6">
<h4 class="text-lg font-semibold mb-4 text-dark">使用场景</h4>
<div class="grid md:grid-cols-2 gap-6">
<div>
<h5 class="font-medium text-gray-700 mb-2">合法用途</h5>
<ul class="space-y-2 text-gray-600">
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>文本格式化和排版控制</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>多语言支持(阿拉伯语、希伯来语等)</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>数据水印和版权保护</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>开发和测试目的</span>
</li>
</ul>
</div>
<div>
<h5 class="font-medium text-gray-700 mb-2">安全风险</h5>
<ul class="space-y-2 text-gray-600">
<li class="flex items-start">
<i class="fa fa-exclamation-triangle text-warning mr-2 mt-1"></i>
<span>隐藏恶意内容和钓鱼攻击</span>
</li>
<li class="flex items-start">
<i class="fa fa-exclamation-triangle text-warning mr-2 mt-1"></i>
<span>绕过内容过滤和验证</span>
</li>
<li class="flex items-start">
<i class="fa fa-exclamation-triangle text-warning mr-2 mt-1"></i>
<span>创建相似但不同的用户名</span>
</li>
<li class="flex items-start">
<i class="fa fa-exclamation-triangle text-warning mr-2 mt-1"></i>
<span>代码注入和安全漏洞</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Security Section -->
<section id="security" class="py-16 bg-white">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<h3 class="text-3xl font-bold mb-8 text-dark">安全知识</h3>
<p class="text-gray-600 mb-8">了解不可见字符的安全风险和防范措施</p>
<div class="grid md:grid-cols-2 gap-8 mb-12">
<div class="bg-gradient-to-br from-orange-50 to-orange-100 rounded-xl p-6 warning-card">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-warning rounded-full flex items-center justify-center mr-3">
<i class="fa fa-exclamation-triangle text-white"></i>
</div>
<h4 class="text-lg font-semibold text-dark">潜在风险</h4>
</div>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fa fa-circle text-xs text-warning mr-2 mt-1.5"></i>
<span>隐藏恶意链接和钓鱼内容</span>
</li>
<li class="flex items-start">
<i class="fa fa-circle text-xs text-warning mr-2 mt-1.5"></i>
<span>绕过用户名和内容验证</span>
</li>
<li class="flex items-start">
<i class="fa fa-circle text-xs text-warning mr-2 mt-1.5"></i>
<span>创建视觉上相同的欺诈性内容</span>
</li>
<li class="flex items-start">
<i class="fa fa-circle text-xs text-warning mr-2 mt-1.5"></i>
<span>代码注入和安全漏洞</span>
</li>
</ul>
</div>
<div class="bg-gradient-to-br from-green-50 to-green-100 rounded-xl p-6">
<div class="flex items-center mb-4">
<div class="w-10 h-10 bg-secondary rounded-full flex items-center justify-center mr-3">
<i class="fa fa-shield text-white"></i>
</div>
<h4 class="text-lg font-semibold text-dark">防范措施</h4>
</div>
<ul class="space-y-3 text-gray-700">
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>使用本工具检测可疑文本</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>验证用户输入中的特殊字符</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>使用字符过滤和净化</span>
</li>
<li class="flex items-start">
<i class="fa fa-check text-secondary mr-2 mt-1"></i>
<span>保持软件和系统更新</span>
</li>
</ul>
</div>
</div>
<div class="bg-gradient-to-r from-primary/10 to-secondary/10 rounded-xl p-6 text-center">
<h4 class="text-xl font-semibold mb-4 text-dark">保护您的文本安全</h4>
<p class="text-gray-600 mb-6">定期使用本工具检查您的文本内容,确保没有隐藏的不可见字符</p>
<button
onclick="scrollToSection('detector')"
class="bg-primary text-white px-8 py-3 rounded-lg font-semibold hover:bg-primary/90 transition-all inline-flex items-center"
>
<i class="fa fa-search mr-2"></i>立即检测
</button>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-dark text-white py-12">
<div class="container mx-auto px-4">
<div class="max-w-4xl mx-auto">
<div class="flex flex-col md:flex-row justify-between items-center mb-8">
<div class="flex items-center space-x-3 mb-4 md:mb-0">
<div class="w-10 h-10 bg-gradient-tech rounded-lg flex items-center justify-center">
<i class="fa fa-eye text-white text-lg"></i>
</div>
<h3 class="text-xl font-bold">不可见字符工具</h3>
</div>
<div class="flex space-x-6">
<a href="#" class="text-white hover:text-primary transition-colors">
<i class="fa fa-github text-xl"></i>
</a>
<a href="#" class="text-white hover:text-primary transition-colors">
<i class="fa fa-twitter text-xl"></i>
</a>
<a href="#" class="text-white hover:text-primary transition-colors">
<i class="fa fa-linkedin text-xl"></i>
</a>
</div>
</div>
<div class="border-t border-gray-700 pt-8">
<div class="grid md:grid-cols-3 gap-8">
<div>
<h4 class="font-semibold mb-4">工具</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#detector" class="hover:text-white transition-colors">字符检测</a></li>
<li><a href="#generator" class="hover:text-white transition-colors">字符生成</a></li>
<li><a href="#reference" class="hover:text-white transition-colors">字符参考</a></li>
</ul>
</div>
<div>
<h4 class="font-semibold mb-4">资源</h4>
<ul class="space-y-2 text-gray-400">
<li><a href="#security" class="hover:text-white transition-colors">安全知识</a></li>
<li><a href="#" class="hover:text-white transition-colors">使用指南</a></li>
<li><a href="#" class="hover:text-white transition-colors">常见问题</a></li>
</ul>
</div>
<div>
<h4 class="font-semibold mb-4">联系我们</h4>
<ul class="space-y-2 text-gray-400">
<li>support@invisiblechars.com</li>
<li>© 2025 不可见字符工具</li>
<li>安全 · 可靠 · 免费</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</footer>
<!-- Toast Notification -->
<div id="toast" class="fixed top-4 right-4 bg-dark text-white px-6 py-3 rounded-lg shadow-lg transform translate-x-full transition-transform duration-300 flex items-center z-50">
<i class="fa fa-check-circle mr-2"></i>
<span id="toastMessage">操作成功</span>
</div>
<script>
// Mobile menu toggle
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const mobileMenu = document.getElementById('mobileMenu');
mobileMenuBtn.addEventListener('click', () => {
if (mobileMenu.classList.contains('-translate-y-full')) {
mobileMenu.classList.remove('-translate-y-full', 'opacity-0');
mobileMenu.classList.add('translate-y-0', 'opacity-100');
} else {
mobileMenu.classList.add('-translate-y-full', 'opacity-0');
mobileMenu.classList.remove('translate-y-0', 'opacity-100');
}
});
// Smooth scrolling for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Close mobile menu if open
if (!mobileMenu.classList.contains('-translate-y-full')) {
mobileMenu.classList.add('-translate-y-full', 'opacity-0');
mobileMenu.classList.remove('translate-y-0', 'opacity-100');
}
}
});
});
// Scroll to section function
function scrollToSection(sectionId) {
const section = document.getElementById(sectionId);
if (section) {
section.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
// Character detector functionality
const inputText = document.getElementById('inputText');
const detectBtn = document.getElementById('detectBtn');
const clearBtn = document.getElementById('clearBtn');
const copyResultBtn = document.getElementById('copyResultBtn');
const visualResult = document.getElementById('visualResult');
const detailedResults = document.getElementById('detailedResults');
const resultsContainer = document.getElementById('resultsContainer');
// Character definitions - only truly invisible characters
const invisibleChars = [
{ char: '\u200B', name: '零宽空格', code: 'U+200B', category: '零宽字符', description: '完全不可见,用于单词分隔' },
{ char: '\u200C', name: '零宽非连接符', code: 'U+200C', category: '零宽字符', description: '完全不可见,防止字符连接' },
{ char: '\u200D', name: '零宽连接符', code: 'U+200D', category: '零宽字符', description: '完全不可见,强制字符连接' },
{ char: '\uFEFF', name: '零宽无断空格 (BOM)', code: 'U+FEFF', category: '零宽字符', description: '完全不可见,字节顺序标记' },
{ char: '\u2060', name: '零宽断行符', code: 'U+2060', category: '控制字符', description: '完全不可见,防止换行' },
{ char: '\u200E', name: '左至右符', code: 'U+200E', category: '控制字符', description: '完全不可见,控制文本方向' },
{ char: '\u200F', name: '右至左符', code: 'U+200F', category: '控制字符', description: '完全不可见,控制文本方向' },
{ char: '\u2061', name: '函数应用符', code: 'U+2061', category: '数学字符', description: '完全不可见,数学符号' },
{ char: '\u2062', name: '不可见乘号', code: 'U+2062', category: '数学字符', description: '完全不可见,数学符号' },
{ char: '\u2063', name: '不可见分隔符', code: 'U+2063', category: '数学字符', description: '完全不可见,数学符号' },
{ char: '\u2064', name: '不可见加号', code: 'U+2064', category: '数学字符', description: '完全不可见,数学符号' },
{ char: '\u034F', name: '组合字符连接符', code: 'U+034F', category: '格式字符', description: '完全不可见,组合字符控制' },
{ char: '\u180E', name: '蒙古语元音分隔符', code: 'U+180E', category: '语言字符', description: '完全不可见,蒙古语支持' },
{ char: '\u2066', name: '左至右隔离符', code: 'U+2066', category: '方向字符', description: '完全不可见,文本方向控制' },
{ char: '\u2067', name: '右至左隔离符', code: 'U+2067', category: '方向字符', description: '完全不可见,文本方向控制' },
{ char: '\u2068', name: '首强隔离符', code: 'U+2068', category: '方向字符', description: '完全不可见,文本方向控制' },
{ char: '\u2069', name: '弹出方向隔离符', code: 'U+2069', category: '方向字符', description: '完全不可见,文本方向控制' }
];
// Detect characters
detectBtn.addEventListener('click', () => {
const text = inputText.value;
if (!text.trim()) {
showToast('请输入要检测的文本', 'warning');
return;
}
const detectedChars = [];
let visualText = '';
let charCount = 0;
// Process each character
for (let i = 0; i < text.length; i++) {
const char = text[i];
const charInfo = invisibleChars.find(c => c.char === char);
if (charInfo) {
// Highlight invisible character
visualText += `<span class="invisible-char" title="${charInfo.name} (${charInfo.code})">${charInfo.code}</span>`;
detectedChars.push({
char: char,
position: i + 1,
name: charInfo.name,
code: charInfo.code,
category: charInfo.category
});
charCount++;
} else {
// Normal character
visualText += char === ' ' ? ' ' : char;
}
}
// Update visual result
visualResult.innerHTML = visualText || '无不可见字符';
// Update detailed results
detailedResults.innerHTML = '';
if (detectedChars.length > 0) {
// Group by category
const categories = {};
detectedChars.forEach(char => {
if (!categories[char.category]) {
categories[char.category] = [];
}
categories[char.category].push(char);
});
// Create result cards
Object.keys(categories).forEach(category => {
const charsInCategory = categories[category];
const card = document.createElement('div');
card.className = 'bg-white rounded-xl shadow-lg p-6 result-card';
card.innerHTML = `
<h5 class="text-lg font-medium mb-3 text-dark">${category} (${charsInCategory.length}个)</h5>
<div class="space-y-2">
${charsInCategory.map(char => `
<div class="flex items-center justify-between p-2 bg-gray-50 rounded">
<div>
<span class="font-semibold text-primary">${char.code}</span>
<span class="text-gray-600 ml-2">${char.name}</span>
</div>
<span class="text-gray-500 text-sm">位置: ${char.position}</span>
</div>
`).join('')}
</div>
`;
detailedResults.appendChild(card);
});
showToast(`检测到 ${charCount} 个不可见字符`, 'success');
} else {
const noResultCard = document.createElement('div');
noResultCard.className = 'bg-white rounded-xl shadow-lg p-6 text-center';
noResultCard.innerHTML = `
<div class="text-secondary text-4xl mb-3">
<i class="fa fa-check-circle"></i>
</div>
<h5 class="text-lg font-medium mb-2 text-dark">未检测到不可见字符</h5>
<p class="text-gray-600">您的文本是干净的,没有隐藏的不可见字符</p>
`;
detailedResults.appendChild(noResultCard);
showToast('未检测到不可见字符', 'success');
}
// Show results container
resultsContainer.classList.remove('hidden');
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
// Clear input
clearBtn.addEventListener('click', () => {
inputText.value = '';
resultsContainer.classList.add('hidden');
showToast('已清空输入', 'info');
});
// Copy result with fallback
copyResultBtn.addEventListener('click', () => {
const textToCopy = inputText.value;
copyToClipboard(textToCopy, '结果已复制到剪贴板');
});
// Generator functionality
const generatedText = document.getElementById('generatedText');
const copyGeneratedBtn = document.getElementById('copyGeneratedBtn');
const clearGeneratedBtn = document.getElementById('clearGeneratedBtn');
// Character cards click event
document.querySelectorAll('.char-card').forEach(card => {
card.addEventListener('click', () => {
const charCode = card.dataset.charCode;
const name = card.dataset.name;
const code = card.dataset.code;
// Create actual character from Unicode code
const char = String.fromCodePoint(parseInt(charCode, 16));
generatedText.value += char;
// Update character count
updateCharCount();
// Add animation feedback
card.classList.add('scale-110');
setTimeout(() => {
card.classList.remove('scale-110');
}, 200);
showToast(`已添加 ${name}`, 'success');
});
});
// Update character count display
function updateCharCount() {
const count = generatedText.value.length;
const charCountBadge = document.getElementById('charCountBadge');
const invisibleHint = document.getElementById('invisibleHint');
if (charCountBadge) {
charCountBadge.textContent = `字符数: ${count}`;
// Change badge color based on count
if (count > 0) {
charCountBadge.className = 'absolute top-2 right-2 bg-secondary/20 text-secondary text-xs px-2 py-1 rounded-full z-20';
} else {
charCountBadge.className = 'absolute top-2 right-2 bg-primary/10 text-primary text-xs px-2 py-1 rounded-full z-20';
}
}
// Show/hide invisible hint
if (invisibleHint) {
if (count > 0 && generatedText.value.trim() === '') {
// Only invisible characters
invisibleHint.classList.remove('hidden');
invisibleHint.className = 'absolute bottom-2 left-4 text-warning text-xs z-20 flex items-center';
} else if (count > 0) {
// Mix of visible and invisible characters
invisibleHint.classList.remove('hidden');
invisibleHint.className = 'absolute bottom-2 left-4 text-gray-500 text-xs z-20 flex items-center';
} else {
invisibleHint.classList.add('hidden');
}
}
}
// Update count when textarea changes
generatedText.addEventListener('input', updateCharCount);
// Initial count update
updateCharCount();
// Copy generated text with fallback
copyGeneratedBtn.addEventListener('click', () => {
const textToCopy = generatedText.value;
if (!textToCopy) {
showToast('没有可复制的内容', 'warning');
return;
}
copyToClipboard(textToCopy, '字符已复制到剪贴板');
});
// Clear generated text
clearGeneratedBtn.addEventListener('click', () => {
generatedText.value = '';
updateCharCount();
showToast('已清空生成内容', 'info');
});
// Toast notification
function showToast(message, type = 'info') {
const toast = document.getElementById('toast');
const toastMessage = document.getElementById('toastMessage');
toastMessage.textContent = message;
// Set icon based on type
const icon = toast.querySelector('i');
icon.className = getToastIcon(type);
// Set background color based on type
toast.className = `fixed top-4 right-4 text-white px-6 py-3 rounded-lg shadow-lg transform translate-x-full transition-transform duration-300 flex items-center z-50 ${getToastBackground(type)}`;
// Show toast
toast.classList.remove('translate-x-full');
toast.classList.add('translate-x-0');
// Hide after 3 seconds
setTimeout(() => {
toast.classList.remove('translate-x-0');
toast.classList.add('translate-x-full');
}, 3000);
}
// Universal copy to clipboard function with fallback
function copyToClipboard(text, successMessage) {
// First try modern clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(() => {
showToast(successMessage, 'success');
}).catch(err => {
console.warn('Clipboard API failed, falling back to textarea method:', err);
fallbackCopyTextToClipboard(text, successMessage);
});
} else {
// Fallback to textarea method
fallbackCopyTextToClipboard(text, successMessage);
}
}
// Fallback copy method using textarea
function fallbackCopyTextToClipboard(text, successMessage) {
const textArea = document.createElement("textarea");
textArea.value = text;
// Make it invisible but still selectable
textArea.style.position = "fixed";
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.width = "2em";
textArea.style.height = "2em";
textArea.style.padding = 0;
textArea.style.border = "none";
textArea.style.outline = "none";
textArea.style.boxShadow = "none";
textArea.style.background = "transparent";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
const successful = document.execCommand('copy');
const msg = successful ? successMessage : '复制失败,请手动复制';
showToast(msg, successful ? 'success' : 'error');
} catch (err) {
console.error('Fallback copy failed:', err);
showToast('复制失败,请手动复制', 'error');
}
document.body.removeChild(textArea);
}
function getToastIcon(type) {
switch(type) {
case 'success': return 'fa fa-check-circle mr-2';
case 'error': return 'fa fa-exclamation-circle mr-2';
case 'warning': return 'fa fa-exclamation-triangle mr-2';
default: return 'fa fa-info-circle mr-2';
}
}
function getToastBackground(type) {
switch(type) {
case 'success': return 'bg-secondary';
case 'error': return 'bg-danger';
case 'warning': return 'bg-warning';
default: return 'bg-primary';
}
}
// Add example text on load
window.addEventListener('load', () => {
// Add example text to input
inputText.placeholder = '例如: "helloworld" (包含零宽空格)';
// Add some example text to try
const exampleBtn = document.createElement('button');
exampleBtn.className = 'absolute right-4 top-4 text-primary hover:text-primary/80 transition-colors';
exampleBtn.innerHTML = '<i class="fa fa-lightbulb-o"></i> 示例';
exampleBtn.onclick = () => {
inputText.value = '这是一个测试文本包含零宽空格和其他不可见字符\u200C';
showToast('已添加示例文本', 'info');
};
inputText.parentElement.style.position = 'relative';
inputText.parentElement.appendChild(exampleBtn);
});
// Add keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl+Enter to detect
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
detectBtn.click();
}
// Ctrl+Shift+C to copy result
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'c') {
e.preventDefault();
copyResultBtn.click();
}
// Ctrl+Shift+V to paste
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'v') {
e.preventDefault();
navigator.clipboard.readText().then(text => {
inputText.value = text;
showToast('已粘贴剪贴板内容', 'info');
});
}
});
// Add scroll animation for sections
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-fade-in');
}
});
}, observerOptions);
// Observe all sections
document.querySelectorAll('section').forEach(section => {
observer.observe(section);
});
// Add hover effects to buttons
document.querySelectorAll('button').forEach(button => {
button.addEventListener('mouseenter', () => {
button.classList.add('shadow-lg');
button.style.transform = 'translateY(-2px)';
});
button.addEventListener('mouseleave', () => {
button.classList.remove('shadow-lg');
button.style.transform = 'translateY(0)';
});
});
// Add loading animation for detect button
detectBtn.addEventListener('click', () => {
const originalText = detectBtn.innerHTML;
detectBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i>检测中...';
detectBtn.disabled = true;
setTimeout(() => {
detectBtn.innerHTML = originalText;
detectBtn.disabled = false;
}, 800);
});
// Responsive adjustments
function adjustForMobile() {
const isMobile = window.innerWidth < 768;
// Adjust textareas height for mobile
if (isMobile) {
inputText.style.height = '120px';
generatedText.style.height = '80px';
} else {
inputText.style.height = '180px';
generatedText.style.height = '120px';
}
}
// Initial adjustment
adjustForMobile();
// Adjust on resize
window.addEventListener('resize', adjustForMobile);
// Add some example text when page loads
window.addEventListener('load', () => {
// Add example text after 2 seconds
setTimeout(() => {
const exampleText = '尝试粘贴包含不可见字符的文本,或者点击"示例"按钮获取测试文本';
if (!inputText.value) {
inputText.placeholder = exampleText;
}
}, 2000);
});
</script>
评论区(暂无评论)