|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. 引言
在现代前端开发中,正则表达式和JavaScript DOM操作是两项核心技能,它们能够帮助开发者创建高效、动态且用户友好的网页。正则表达式提供了强大的文本模式匹配和处理能力,而DOM操作则允许我们动态地修改网页内容和结构。掌握这两项技术并学会将它们结合使用,可以极大地提升前端开发效率和用户体验。
本文将从基础概念开始,逐步深入到实际应用场景,通过详细的代码示例和实战案例,帮助读者全面理解并掌握正则表达式与JavaScript DOM操作的结合使用,从而提升前端开发技能。
2. 正则表达式基础
2.1 什么是正则表达式
正则表达式(Regular Expression,简称regex或regexp)是一种用于描述字符串模式的强大工具。它由一系列特殊字符和普通字符组成,可以用来检查字符串是否符合某种模式、提取符合模式的部分或替换字符串中的特定部分。
2.2 正则表达式的基本语法
在JavaScript中,可以通过两种方式创建正则表达式:
- // 使用字面量
- const pattern1 = /pattern/flags;
- // 使用构造函数
- const pattern2 = new RegExp('pattern', 'flags');
复制代码
• .:匹配除换行符以外的任何单个字符
• \d:匹配任何数字(等同于[0-9])
• \D:匹配任何非数字字符(等同于[^0-9])
• \w:匹配任何字母数字字符(等同于[a-zA-Z0-9_])
• \W:匹配任何非字母数字字符(等同于[^a-zA-Z0-9_])
• \s:匹配任何空白字符(包括空格、制表符、换行符等)
• \S:匹配任何非空白字符
• *:匹配前面的元素零次或多次
• +:匹配前面的元素一次或多次
• ?:匹配前面的元素零次或一次
• {n}:匹配前面的元素恰好n次
• {n,}:匹配前面的元素至少n次
• {n,m}:匹配前面的元素至少n次,至多m次
• ^:匹配字符串的开始
• $:匹配字符串的结束
• \b:匹配单词边界
• \B:匹配非单词边界
• [abc]:匹配a、b或c中的任意一个字符
• [^abc]:匹配除a、b、c以外的任何字符
• [a-z]:匹配a到z之间的任何小写字母
• [A-Z]:匹配A到Z之间的任何大写字母
• [0-9]:匹配0到9之间的任何数字
2.3 常用的正则表达式模式
以下是一些常用的正则表达式模式示例:
- // 邮箱验证
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- // 手机号验证(简单版)
- const phoneRegex = /^1[3-9]\d{9}$/;
- // URL验证
- const urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
- // 身份证号验证(简单版)
- const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
- // 密码强度验证(至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符,长度至少8位)
- const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
复制代码
2.4 JavaScript中的正则表达式方法
JavaScript提供了多种使用正则表达式的方法:
- // test():测试字符串是否匹配模式,返回true或false
- const pattern = /hello/;
- console.log(pattern.test('hello world')); // true
- // exec():在字符串中执行匹配搜索,返回结果数组或null
- const pattern = /(\d+)\.(\d+)\.(\d+)/;
- const version = 'Version: 2.5.10';
- const result = pattern.exec(version);
- console.log(result);
- // 输出: ["2.5.10", "2", "5", "10", index: 9, input: "Version: 2.5.10", groups: undefined]
复制代码- // match():在字符串中查找匹配,返回结果数组或null
- const text = 'The price is $123.45';
- const result = text.match(/\d+\.\d+/);
- console.log(result); // ["123.45", index: 12, input: "The price is $123.45", groups: undefined]
- // search():在字符串中查找匹配,返回匹配位置的索引,否则返回-1
- const text = 'Hello world';
- console.log(text.search(/world/)); // 6
- // replace():替换匹配的子串
- const text = 'Hello world';
- console.log(text.replace(/world/, 'JavaScript')); // "Hello JavaScript"
- // split():使用匹配的子串作为分隔符来分割字符串
- const text = 'apple,banana,orange';
- console.log(text.split(/,/)); // ["apple", "banana", "orange"]
复制代码
3. JavaScript DOM操作基础
3.1 DOM的概念和结构
文档对象模型(Document Object Model,简称DOM)是HTML和XML文档的编程接口。它将文档表示为一个节点树,其中每个节点代表文档的一部分(如元素、属性、文本等)。
DOM树的结构如下:
- document
- └── html
- ├── head
- │ ├── title
- │ ├── meta
- │ └── link
- └── body
- ├── header
- ├── nav
- ├── main
- │ ├── h1
- │ ├── p
- │ └── div
- └── footer
复制代码
3.2 选择DOM元素的方法
JavaScript提供了多种选择DOM元素的方法:
- // 通过ID选择元素
- const elementById = document.getElementById('myId');
- // 通过类名选择元素(返回HTMLCollection)
- const elementsByClass = document.getElementsByClassName('myClass');
- // 通过标签名选择元素(返回HTMLCollection)
- const elementsByTag = document.getElementsByTagName('div');
- // 通过CSS选择器选择单个元素
- const elementByQuery = document.querySelector('#myId .myClass');
- // 通过CSS选择器选择多个元素(返回NodeList)
- const elementsByQueryAll = document.querySelectorAll('div.myClass');
复制代码
3.3 修改DOM元素
- // 获取元素
- const element = document.getElementById('myElement');
- // 修改文本内容
- element.textContent = '新的文本内容';
- // 修改HTML内容
- element.innerHTML = '<strong>加粗的文本</strong>';
- // 修改属性
- element.setAttribute('class', 'new-class');
- element.id = 'newId';
- // 修改样式
- element.style.color = 'red';
- element.style.fontSize = '16px';
复制代码- const element = document.getElementById('myElement');
- // 添加类
- element.classList.add('active');
- // 移除类
- element.classList.remove('inactive');
- // 切换类(如果存在则移除,不存在则添加)
- element.classList.toggle('highlight');
- // 检查是否包含某个类
- if (element.classList.contains('active')) {
- console.log('元素具有active类');
- }
复制代码
3.4 创建和删除DOM元素
- // 创建新元素
- const newDiv = document.createElement('div');
- newDiv.id = 'newDiv';
- newDiv.className = 'container';
- newDiv.textContent = '这是一个新创建的div';
- // 添加到DOM中
- document.body.appendChild(newDiv);
- // 创建文本节点
- const textNode = document.createTextNode('这是一个文本节点');
- newDiv.appendChild(textNode);
- // 删除元素
- const elementToRemove = document.getElementById('elementToRemove');
- elementToRemove.parentNode.removeChild(elementToRemove);
- // 替换元素
- const oldElement = document.getElementById('oldElement');
- const newElement = document.createElement('div');
- newElement.textContent = '新元素';
- oldElement.parentNode.replaceChild(newElement, oldElement);
复制代码
3.5 事件处理
- // 获取元素
- const button = document.getElementById('myButton');
- // 添加事件监听器
- button.addEventListener('click', function(event) {
- console.log('按钮被点击了');
- console.log('事件对象:', event);
- });
- // 使用箭头函数
- button.addEventListener('click', (event) => {
- console.log('按钮被点击了(箭头函数)');
- });
- // 移除事件监听器
- function handleClick(event) {
- console.log('处理点击事件');
- }
- button.addEventListener('click', handleClick);
- // button.removeEventListener('click', handleClick);
- // 事件委托
- document.addEventListener('click', function(event) {
- if (event.target.matches('.button')) {
- console.log('某个按钮被点击了');
- }
- });
复制代码
4. 结合正则表达式和DOM操作
4.1 表单验证
正则表达式和DOM操作结合最常见的应用场景之一是表单验证。以下是一个完整的表单验证示例:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>表单验证示例</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 600px;
- margin: 0 auto;
- padding: 20px;
- }
- .form-group {
- margin-bottom: 15px;
- }
- label {
- display: block;
- margin-bottom: 5px;
- font-weight: bold;
- }
- input {
- width: 100%;
- padding: 8px;
- box-sizing: border-box;
- border: 1px solid #ddd;
- border-radius: 4px;
- }
- input:focus {
- outline: none;
- border-color: #4CAF50;
- }
- .error {
- color: red;
- font-size: 12px;
- margin-top: 5px;
- display: none;
- }
- input.invalid {
- border-color: red;
- }
- button {
- background-color: #4CAF50;
- color: white;
- padding: 10px 15px;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- }
- button:hover {
- background-color: #45a049;
- }
- .success-message {
- display: none;
- background-color: #dff0d8;
- color: #3c763d;
- padding: 10px;
- margin-top: 20px;
- border-radius: 4px;
- }
- </style>
- </head>
- <body>
- <h1>注册表单</h1>
- <form id="registrationForm">
- <div class="form-group">
- <label for="username">用户名:</label>
- <input type="text" id="username" name="username">
- <div id="usernameError" class="error">用户名必须是4-16个字母、数字或下划线</div>
- </div>
-
- <div class="form-group">
- <label for="email">邮箱:</label>
- <input type="email" id="email" name="email">
- <div id="emailError" class="error">请输入有效的邮箱地址</div>
- </div>
-
- <div class="form-group">
- <label for="phone">手机号:</label>
- <input type="tel" id="phone" name="phone">
- <div id="phoneError" class="error">请输入有效的11位手机号</div>
- </div>
-
- <div class="form-group">
- <label for="password">密码:</label>
- <input type="password" id="password" name="password">
- <div id="passwordError" class="error">密码必须至少8位,包含大小写字母、数字和特殊字符</div>
- </div>
-
- <div class="form-group">
- <label for="confirmPassword">确认密码:</label>
- <input type="password" id="confirmPassword" name="confirmPassword">
- <div id="confirmPasswordError" class="error">两次输入的密码不一致</div>
- </div>
-
- <button type="submit">注册</button>
- </form>
-
- <div id="successMessage" class="success-message">
- 注册成功!
- </div>
- <script>
- // 获取表单和输入元素
- const form = document.getElementById('registrationForm');
- const usernameInput = document.getElementById('username');
- const emailInput = document.getElementById('email');
- const phoneInput = document.getElementById('phone');
- const passwordInput = document.getElementById('password');
- const confirmPasswordInput = document.getElementById('confirmPassword');
-
- // 获取错误消息元素
- const usernameError = document.getElementById('usernameError');
- const emailError = document.getElementById('emailError');
- const phoneError = document.getElementById('phoneError');
- const passwordError = document.getElementById('passwordError');
- const confirmPasswordError = document.getElementById('confirmPasswordError');
-
- // 获取成功消息元素
- const successMessage = document.getElementById('successMessage');
-
- // 定义正则表达式
- const usernameRegex = /^[a-zA-Z0-9_]{4,16}$/;
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- const phoneRegex = /^1[3-9]\d{9}$/;
- const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
-
- // 验证函数
- function validateUsername() {
- const isValid = usernameRegex.test(usernameInput.value);
- if (!isValid) {
- usernameInput.classList.add('invalid');
- usernameError.style.display = 'block';
- } else {
- usernameInput.classList.remove('invalid');
- usernameError.style.display = 'none';
- }
- return isValid;
- }
-
- function validateEmail() {
- const isValid = emailRegex.test(emailInput.value);
- if (!isValid) {
- emailInput.classList.add('invalid');
- emailError.style.display = 'block';
- } else {
- emailInput.classList.remove('invalid');
- emailError.style.display = 'none';
- }
- return isValid;
- }
-
- function validatePhone() {
- const isValid = phoneRegex.test(phoneInput.value);
- if (!isValid) {
- phoneInput.classList.add('invalid');
- phoneError.style.display = 'block';
- } else {
- phoneInput.classList.remove('invalid');
- phoneError.style.display = 'none';
- }
- return isValid;
- }
-
- function validatePassword() {
- const isValid = passwordRegex.test(passwordInput.value);
- if (!isValid) {
- passwordInput.classList.add('invalid');
- passwordError.style.display = 'block';
- } else {
- passwordInput.classList.remove('invalid');
- passwordError.style.display = 'none';
- }
- return isValid;
- }
-
- function validateConfirmPassword() {
- const isValid = passwordInput.value === confirmPasswordInput.value;
- if (!isValid) {
- confirmPasswordInput.classList.add('invalid');
- confirmPasswordError.style.display = 'block';
- } else {
- confirmPasswordInput.classList.remove('invalid');
- confirmPasswordError.style.display = 'none';
- }
- return isValid;
- }
-
- // 添加输入事件监听器
- usernameInput.addEventListener('input', validateUsername);
- emailInput.addEventListener('input', validateEmail);
- phoneInput.addEventListener('input', validatePhone);
- passwordInput.addEventListener('input', validatePassword);
- confirmPasswordInput.addEventListener('input', validateConfirmPassword);
-
- // 添加表单提交事件监听器
- form.addEventListener('submit', function(event) {
- event.preventDefault();
-
- // 验证所有字段
- const isUsernameValid = validateUsername();
- const isEmailValid = validateEmail();
- const isPhoneValid = validatePhone();
- const isPasswordValid = validatePassword();
- const isConfirmPasswordValid = validateConfirmPassword();
-
- // 如果所有字段都有效,显示成功消息
- if (isUsernameValid && isEmailValid && isPhoneValid && isPasswordValid && isConfirmPasswordValid) {
- form.style.display = 'none';
- successMessage.style.display = 'block';
- }
- });
- </script>
- </body>
- </html>
复制代码
4.2 动态内容搜索和过滤
正则表达式和DOM操作结合的另一个常见应用是动态内容搜索和过滤。以下是一个实时搜索和过滤列表项的示例:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>动态搜索和过滤</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- .search-container {
- margin-bottom: 20px;
- }
- .search-box {
- width: 100%;
- padding: 10px;
- font-size: 16px;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-sizing: border-box;
- }
- .search-options {
- margin-top: 10px;
- display: flex;
- gap: 15px;
- }
- .search-option {
- display: flex;
- align-items: center;
- }
- .search-option input {
- margin-right: 5px;
- }
- .item-list {
- list-style-type: none;
- padding: 0;
- }
- .item {
- padding: 15px;
- border-bottom: 1px solid #eee;
- display: flex;
- align-items: center;
- }
- .item:last-child {
- border-bottom: none;
- }
- .item-icon {
- width: 50px;
- height: 50px;
- background-color: #4CAF50;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-weight: bold;
- margin-right: 15px;
- }
- .item-content {
- flex: 1;
- }
- .item-title {
- font-weight: bold;
- margin-bottom: 5px;
- }
- .item-description {
- color: #666;
- font-size: 14px;
- }
- .highlight {
- background-color: yellow;
- font-weight: bold;
- }
- .no-results {
- text-align: center;
- padding: 20px;
- color: #666;
- display: none;
- }
- </style>
- </head>
- <body>
- <h1>联系人列表</h1>
-
- <div class="search-container">
- <input type="text" id="searchInput" class="search-box" placeholder="搜索联系人...">
- <div class="search-options">
- <div class="search-option">
- <input type="checkbox" id="caseSensitive" name="caseSensitive">
- <label for="caseSensitive">区分大小写</label>
- </div>
- <div class="search-option">
- <input type="checkbox" id="wholeWord" name="wholeWord">
- <label for="wholeWord">全词匹配</label>
- </div>
- <div class="search-option">
- <input type="checkbox" id="useRegex" name="useRegex">
- <label for="useRegex">使用正则表达式</label>
- </div>
- </div>
- </div>
-
- <ul id="itemList" class="item-list">
- <li class="item" data-name="张三" data-phone="13800138000" data-email="zhangsan@example.com">
- <div class="item-icon">张</div>
- <div class="item-content">
- <div class="item-title">张三</div>
- <div class="item-description">电话: 13800138000 | 邮箱: zhangsan@example.com</div>
- </div>
- </li>
- <li class="item" data-name="李四" data-phone="13900139000" data-email="lisi@example.com">
- <div class="item-icon">李</div>
- <div class="item-content">
- <div class="item-title">李四</div>
- <div class="item-description">电话: 13900139000 | 邮箱: lisi@example.com</div>
- </div>
- </li>
- <li class="item" data-name="王五" data-phone="13700137000" data-email="wangwu@example.com">
- <div class="item-icon">王</div>
- <div class="item-content">
- <div class="item-title">王五</div>
- <div class="item-description">电话: 13700137000 | 邮箱: wangwu@example.com</div>
- </div>
- </li>
- <li class="item" data-name="赵六" data-phone="13600136000" data-email="zhaoliu@example.com">
- <div class="item-icon">赵</div>
- <div class="item-content">
- <div class="item-title">赵六</div>
- <div class="item-description">电话: 13600136000 | 邮箱: zhaoliu@example.com</div>
- </div>
- </li>
- <li class="item" data-name="钱七" data-phone="13500135000" data-email="qianqi@example.com">
- <div class="item-icon">钱</div>
- <div class="item-content">
- <div class="item-title">钱七</div>
- <div class="item-description">电话: 13500135000 | 邮箱: qianqi@example.com</div>
- </div>
- </li>
- </ul>
-
- <div id="noResults" class="no-results">
- 没有找到匹配的联系人
- </div>
- <script>
- // 获取DOM元素
- const searchInput = document.getElementById('searchInput');
- const caseSensitiveCheckbox = document.getElementById('caseSensitive');
- const wholeWordCheckbox = document.getElementById('wholeWord');
- const useRegexCheckbox = document.getElementById('useRegex');
- const itemList = document.getElementById('itemList');
- const items = itemList.querySelectorAll('.item');
- const noResults = document.getElementById('noResults');
-
- // 搜索函数
- function performSearch() {
- const searchTerm = searchInput.value.trim();
- const caseSensitive = caseSensitiveCheckbox.checked;
- const wholeWord = wholeWordCheckbox.checked;
- const useRegex = useRegexCheckbox.checked;
-
- let hasResults = false;
-
- // 如果搜索词为空,显示所有项目
- if (searchTerm === '') {
- items.forEach(item => {
- item.style.display = 'flex';
- // 移除所有高亮
- removeHighlights(item);
- });
- noResults.style.display = 'none';
- return;
- }
-
- // 构建正则表达式
- let pattern;
- try {
- if (useRegex) {
- // 使用用户提供的正则表达式
- pattern = new RegExp(searchTerm, caseSensitive ? 'g' : 'gi');
- } else {
- // 构建基于搜索词的正则表达式
- let escapedTerm = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
- if (wholeWord) {
- pattern = new RegExp(`\\b${escapedTerm}\\b`, caseSensitive ? 'g' : 'gi');
- } else {
- pattern = new RegExp(escapedTerm, caseSensitive ? 'g' : 'gi');
- }
- }
-
- // 遍历所有项目
- items.forEach(item => {
- const name = item.getAttribute('data-name');
- const phone = item.getAttribute('data-phone');
- const email = item.getAttribute('data-email');
- const titleElement = item.querySelector('.item-title');
- const descriptionElement = item.querySelector('.item-description');
-
- // 检查是否匹配
- const nameMatch = pattern.test(name);
- pattern.lastIndex = 0; // 重置正则表达式的lastIndex
- const phoneMatch = pattern.test(phone);
- pattern.lastIndex = 0;
- const emailMatch = pattern.test(email);
- pattern.lastIndex = 0;
-
- if (nameMatch || phoneMatch || emailMatch) {
- // 显示匹配的项目
- item.style.display = 'flex';
- hasResults = true;
-
- // 移除之前的高亮
- removeHighlights(item);
-
- // 添加新的高亮
- if (nameMatch) {
- highlightText(titleElement, pattern);
- }
- if (phoneMatch || emailMatch) {
- highlightText(descriptionElement, pattern);
- }
- } else {
- // 隐藏不匹配的项目
- item.style.display = 'none';
- }
- });
-
- // 显示或隐藏"无结果"消息
- noResults.style.display = hasResults ? 'none' : 'block';
-
- } catch (error) {
- // 处理正则表达式错误
- console.error('正则表达式错误:', error);
- items.forEach(item => {
- item.style.display = 'flex';
- removeHighlights(item);
- });
- noResults.style.display = 'none';
- }
- }
-
- // 高亮文本函数
- function highlightText(element, regex) {
- const text = element.textContent;
- const matches = text.match(regex);
-
- if (matches) {
- let highlightedText = text;
- matches.forEach(match => {
- // 使用正则表达式全局替换匹配的文本
- highlightedText = highlightedText.replace(regex, '<span class="highlight">$&</span>');
- });
- element.innerHTML = highlightedText;
- }
- }
-
- // 移除高亮函数
- function removeHighlights(item) {
- const highlights = item.querySelectorAll('.highlight');
- highlights.forEach(highlight => {
- const parent = highlight.parentNode;
- parent.replaceChild(document.createTextNode(highlight.textContent), highlight);
- parent.normalize();
- });
- }
-
- // 添加事件监听器
- searchInput.addEventListener('input', performSearch);
- caseSensitiveCheckbox.addEventListener('change', performSearch);
- wholeWordCheckbox.addEventListener('change', performSearch);
- useRegexCheckbox.addEventListener('change', performSearch);
- </script>
- </body>
- </html>
复制代码
4.3 文本处理和格式化
正则表达式和DOM操作结合还可以用于文本处理和格式化。以下是一个文本格式化工具的示例:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>文本格式化工具</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- h1 {
- text-align: center;
- }
- .container {
- display: flex;
- flex-direction: column;
- gap: 20px;
- }
- .section {
- border: 1px solid #ddd;
- border-radius: 5px;
- padding: 15px;
- }
- .section-title {
- font-weight: bold;
- margin-bottom: 10px;
- color: #333;
- }
- textarea {
- width: 100%;
- height: 150px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
- resize: vertical;
- font-family: inherit;
- }
- .buttons {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- margin-top: 10px;
- }
- button {
- padding: 8px 12px;
- background-color: #4CAF50;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-size: 14px;
- }
- button:hover {
- background-color: #45a049;
- }
- .options {
- display: flex;
- flex-wrap: wrap;
- gap: 15px;
- margin-top: 10px;
- }
- .option {
- display: flex;
- align-items: center;
- }
- .option input {
- margin-right: 5px;
- }
- .custom-regex {
- margin-top: 10px;
- }
- .custom-regex input {
- width: 100%;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 4px;
- }
- </style>
- </head>
- <body>
- <h1>文本格式化工具</h1>
-
- <div class="container">
- <div class="section">
- <div class="section-title">输入文本</div>
- <textarea id="inputText" placeholder="在此输入要格式化的文本..."></textarea>
- </div>
-
- <div class="section">
- <div class="section-title">格式化选项</div>
- <div class="buttons">
- <button id="removeExtraSpaces">移除多余空格</button>
- <button id="fixLineBreaks">修正换行</button>
- <button id="capitalizeSentences">句子首字母大写</button>
- <button id="removeSpecialChars">移除特殊字符</button>
- <button id="formatPhoneNumbers">格式化电话号码</button>
- <button id="formatEmails">格式化邮箱地址</button>
- <button id="extractUrls">提取URL</button>
- <button id="extractNumbers">提取数字</button>
- </div>
-
- <div class="options">
- <div class="option">
- <input type="checkbox" id="globalReplace">
- <label for="globalReplace">全局替换</label>
- </div>
- <div class="option">
- <input type="checkbox" id="caseSensitive">
- <label for="caseSensitive">区分大小写</label>
- </div>
- </div>
-
- <div class="custom-regex">
- <label for="customRegex">自定义正则表达式:</label>
- <input type="text" id="customRegex" placeholder="输入正则表达式">
- <button id="applyCustomRegex">应用</button>
- </div>
- </div>
-
- <div class="section">
- <div class="section-title">格式化结果</div>
- <textarea id="outputText" readonly placeholder="格式化结果将显示在这里..."></textarea>
- <div class="buttons">
- <button id="copyResult">复制结果</button>
- <button id="clearAll">清除全部</button>
- </div>
- </div>
- </div>
- <script>
- // 获取DOM元素
- const inputText = document.getElementById('inputText');
- const outputText = document.getElementById('outputText');
- const globalReplaceCheckbox = document.getElementById('globalReplace');
- const caseSensitiveCheckbox = document.getElementById('caseSensitive');
- const customRegexInput = document.getElementById('customRegex');
-
- // 获取按钮
- const removeExtraSpacesBtn = document.getElementById('removeExtraSpaces');
- const fixLineBreaksBtn = document.getElementById('fixLineBreaks');
- const capitalizeSentencesBtn = document.getElementById('capitalizeSentences');
- const removeSpecialCharsBtn = document.getElementById('removeSpecialChars');
- const formatPhoneNumbersBtn = document.getElementById('formatPhoneNumbers');
- const formatEmailsBtn = document.getElementById('formatEmails');
- const extractUrlsBtn = document.getElementById('extractUrls');
- const extractNumbersBtn = document.getElementById('extractNumbers');
- const applyCustomRegexBtn = document.getElementById('applyCustomRegex');
- const copyResultBtn = document.getElementById('copyResult');
- const clearAllBtn = document.getElementById('clearAll');
-
- // 获取标志
- function getFlags() {
- let flags = '';
- if (globalReplaceCheckbox.checked) flags += 'g';
- if (!caseSensitiveCheckbox.checked) flags += 'i';
- return flags;
- }
-
- // 应用正则表达式
- function applyRegex(pattern, replacement, input = inputText.value) {
- try {
- const regex = new RegExp(pattern, getFlags());
- return input.replace(regex, replacement);
- } catch (error) {
- console.error('正则表达式错误:', error);
- return '正则表达式错误: ' + error.message;
- }
- }
-
- // 移除多余空格
- removeExtraSpacesBtn.addEventListener('click', function() {
- // 移除开头和结尾的空格
- let result = inputText.value.trim();
- // 将多个连续空格替换为单个空格
- result = result.replace(/ +/g, ' ');
- // 将多个连续换行符替换为单个换行符
- result = result.replace(/\n+/g, '\n');
- outputText.value = result;
- });
-
- // 修正换行
- fixLineBreaksBtn.addEventListener('click', function() {
- // 将各种换行符统一为\n
- let result = inputText.value.replace(/\r\n|\r/g, '\n');
- // 确保段落之间只有一个空行
- result = result.replace(/\n{3,}/g, '\n\n');
- outputText.value = result;
- });
-
- // 句子首字母大写
- capitalizeSentencesBtn.addEventListener('click', function() {
- let result = inputText.value.toLowerCase();
- // 句子首字母大写
- result = result.replace(/(^\w|\.\s*\w)/g, function(match) {
- return match.toUpperCase();
- });
- outputText.value = result;
- });
-
- // 移除特殊字符
- removeSpecialCharsBtn.addEventListener('click', function() {
- // 保留字母、数字、中文、基本标点和空格
- const result = applyRegex(/[^\w\s\u4e00-\u9fa5.,!?;:()\[\]{}'"\/\-]/g, '');
- outputText.value = result;
- });
-
- // 格式化电话号码
- formatPhoneNumbersBtn.addEventListener('click', function() {
- // 将连续的11位数字格式化为手机号格式
- let result = applyRegex(/(\d{3})(\d{4})(\d{4})/g, '$1-$2-$3');
- // 将连续的3-4位数字后跟7-8位数字格式化为座机格式
- result = applyRegex(/(\d{3,4})(\d{7,8})/g, '$1-$2', result);
- outputText.value = result;
- });
-
- // 格式化邮箱地址
- formatEmailsBtn.addEventListener('click', function() {
- // 将邮箱地址转换为小写
- const result = applyRegex(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g, function(match) {
- return match.toLowerCase();
- });
- outputText.value = result;
- });
-
- // 提取URL
- extractUrlsBtn.addEventListener('click', function() {
- // 提取URL
- const urls = inputText.value.match(/https?:\/\/[^\s]+/g);
- outputText.value = urls ? urls.join('\n') : '未找到URL';
- });
-
- // 提取数字
- extractNumbersBtn.addEventListener('click', function() {
- // 提取所有数字
- const numbers = inputText.value.match(/\d+\.?\d*/g);
- outputText.value = numbers ? numbers.join('\n') : '未找到数字';
- });
-
- // 应用自定义正则表达式
- applyCustomRegexBtn.addEventListener('click', function() {
- const pattern = customRegexInput.value.trim();
- if (!pattern) {
- outputText.value = '请输入正则表达式';
- return;
- }
-
- // 简单替换模式
- const result = applyRegex(pattern, '');
- outputText.value = result;
- });
-
- // 复制结果
- copyResultBtn.addEventListener('click', function() {
- outputText.select();
- document.execCommand('copy');
-
- // 临时更改按钮文本
- const originalText = copyResultBtn.textContent;
- copyResultBtn.textContent = '已复制!';
- setTimeout(() => {
- copyResultBtn.textContent = originalText;
- }, 2000);
- });
-
- // 清除全部
- clearAllBtn.addEventListener('click', function() {
- inputText.value = '';
- outputText.value = '';
- customRegexInput.value = '';
- });
- </script>
- </body>
- </html>
复制代码
4.4 动态样式应用
正则表达式和DOM操作结合还可以用于动态样式应用。以下是一个根据内容模式动态应用样式的示例:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>动态样式应用</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 800px;
- margin: 0 auto;
- padding: 20px;
- }
- h1 {
- text-align: center;
- }
- .controls {
- background-color: #f5f5f5;
- padding: 15px;
- border-radius: 5px;
- margin-bottom: 20px;
- }
- .control-group {
- margin-bottom: 15px;
- }
- .control-group label {
- display: block;
- margin-bottom: 5px;
- font-weight: bold;
- }
- .control-group input, .control-group select {
- width: 100%;
- padding: 8px;
- border: 1px solid #ddd;
- border-radius: 4px;
- box-sizing: border-box;
- }
- .button-group {
- display: flex;
- gap: 10px;
- margin-top: 10px;
- }
- button {
- padding: 8px 12px;
- background-color: #4CAF50;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- }
- button:hover {
- background-color: #45a049;
- }
- .content-area {
- border: 1px solid #ddd;
- border-radius: 5px;
- padding: 15px;
- min-height: 200px;
- }
- .highlight {
- background-color: yellow;
- font-weight: bold;
- }
- .highlight-red {
- background-color: #ffcccc;
- color: #990000;
- }
- .highlight-green {
- background-color: #ccffcc;
- color: #006600;
- }
- .highlight-blue {
- background-color: #ccccff;
- color: #000099;
- }
- .underline {
- text-decoration: underline;
- }
- .italic {
- font-style: italic;
- }
- .bold {
- font-weight: bold;
- }
- .uppercase {
- text-transform: uppercase;
- }
- .lowercase {
- text-transform: lowercase;
- }
- .message {
- margin-top: 10px;
- padding: 10px;
- border-radius: 4px;
- display: none;
- }
- .success {
- background-color: #dff0d8;
- color: #3c763d;
- }
- .error {
- background-color: #f2dede;
- color: #a94442;
- }
- </style>
- </head>
- <body>
- <h1>动态样式应用工具</h1>
-
- <div class="controls">
- <div class="control-group">
- <label for="patternInput">模式 (正则表达式):</label>
- <input type="text" id="patternInput" placeholder="输入正则表达式,例如: \d+">
- </div>
-
- <div class="control-group">
- <label for="styleSelect">应用样式:</label>
- <select id="styleSelect">
- <option value="highlight">高亮 (黄色)</option>
- <option value="highlight-red">高亮 (红色)</option>
- <option value="highlight-green">高亮 (绿色)</option>
- <option value="highlight-blue">高亮 (蓝色)</option>
- <option value="underline">下划线</option>
- <option value="italic">斜体</option>
- <option value="bold">粗体</option>
- <option value="uppercase">大写</option>
- <option value="lowercase">小写</option>
- </select>
- </div>
-
- <div class="control-group">
- <label>
- <input type="checkbox" id="caseSensitive"> 区分大小写
- </label>
- </div>
-
- <div class="button-group">
- <button id="applyStyle">应用样式</button>
- <button id="clearStyles">清除所有样式</button>
- <button id="presetExample">使用示例</button>
- </div>
-
- <div id="message" class="message"></div>
- </div>
-
- <div class="content-area" id="contentArea">
- <h2>示例文本</h2>
- <p>这是一段示例文本,用于演示动态样式应用功能。您可以输入正则表达式来匹配文本中的特定模式,然后选择要应用的样式。</p>
- <p>例如,您可以输入 <code>\d+</code> 来匹配所有的数字,然后选择"高亮 (黄色)"样式来高亮显示这些数字。</p>
- <p>联系电话: 138-1234-5678 或 010-12345678</p>
- <p>电子邮箱: example@example.com</p>
- <p>网址: https://www.example.com</p>
- <p>日期: 2023-05-15</p>
- <p>价格: $19.99 或 ¥99.00</p>
- </div>
- <script>
- // 获取DOM元素
- const patternInput = document.getElementById('patternInput');
- const styleSelect = document.getElementById('styleSelect');
- const caseSensitiveCheckbox = document.getElementById('caseSensitive');
- const contentArea = document.getElementById('contentArea');
- const messageDiv = document.getElementById('message');
-
- // 获取按钮
- const applyStyleBtn = document.getElementById('applyStyle');
- const clearStylesBtn = document.getElementById('clearStyles');
- const presetExampleBtn = document.getElementById('presetExample');
-
- // 显示消息
- function showMessage(text, type) {
- messageDiv.textContent = text;
- messageDiv.className = 'message ' + type;
- messageDiv.style.display = 'block';
-
- // 3秒后隐藏消息
- setTimeout(() => {
- messageDiv.style.display = 'none';
- }, 3000);
- }
-
- // 应用样式
- function applyStyle() {
- const pattern = patternInput.value.trim();
- if (!pattern) {
- showMessage('请输入正则表达式模式', 'error');
- return;
- }
-
- const style = styleSelect.value;
- const caseSensitive = caseSensitiveCheckbox.checked;
-
- try {
- // 创建正则表达式
- const flags = caseSensitive ? 'g' : 'gi';
- const regex = new RegExp(pattern, flags);
-
- // 获取内容区域的所有文本节点
- const walker = document.createTreeWalker(
- contentArea,
- NodeFilter.SHOW_TEXT,
- null,
- false
- );
-
- let node;
- const nodesToProcess = [];
-
- // 收集所有文本节点
- while (node = walker.nextNode()) {
- // 跳过脚本和样式元素内的文本
- if (node.parentNode.tagName !== 'SCRIPT' && node.parentNode.tagName !== 'STYLE') {
- nodesToProcess.push(node);
- }
- }
-
- // 处理每个文本节点
- nodesToProcess.forEach(textNode => {
- const text = textNode.textContent;
- let match;
- let lastIndex = 0;
- const fragment = document.createDocumentFragment();
-
- // 重置正则表达式的lastIndex
- regex.lastIndex = 0;
-
- while ((match = regex.exec(text)) !== null) {
- // 添加匹配前的文本
- if (match.index > lastIndex) {
- fragment.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
- }
-
- // 创建匹配文本的span并应用样式
- const span = document.createElement('span');
- span.className = style;
- span.textContent = match[0];
- fragment.appendChild(span);
-
- lastIndex = regex.lastIndex;
- }
-
- // 添加剩余的文本
- if (lastIndex < text.length) {
- fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
- }
-
- // 替换原始文本节点
- if (fragment.childNodes.length > 1 || (fragment.childNodes.length === 1 && fragment.firstChild.nodeType !== Node.TEXT_NODE)) {
- textNode.parentNode.replaceChild(fragment, textNode);
- }
- });
-
- showMessage('样式应用成功', 'success');
- } catch (error) {
- showMessage('正则表达式错误: ' + error.message, 'error');
- console.error('正则表达式错误:', error);
- }
- }
-
- // 清除所有样式
- function clearStyles() {
- // 获取所有带有样式的span元素
- const styledSpans = contentArea.querySelectorAll('span[class*="highlight"], span.underline, span.italic, span.bold, span.uppercase, span.lowercase');
-
- styledSpans.forEach(span => {
- const parent = span.parentNode;
-
- // 用文本节点替换span元素
- parent.replaceChild(document.createTextNode(span.textContent), span);
-
- // 合并相邻的文本节点
- parent.normalize();
- });
-
- showMessage('已清除所有样式', 'success');
- }
-
- // 使用示例
- function usePresetExample() {
- patternInput.value = '\\d+'; // 匹配数字
- styleSelect.value = 'highlight-blue'; // 蓝色高亮
- caseSensitiveCheckbox.checked = false;
-
- // 应用样式
- applyStyle();
- }
-
- // 添加事件监听器
- applyStyleBtn.addEventListener('click', applyStyle);
- clearStylesBtn.addEventListener('click', clearStyles);
- presetExampleBtn.addEventListener('click', usePresetExample);
-
- // 添加回车键支持
- patternInput.addEventListener('keypress', function(event) {
- if (event.key === 'Enter') {
- applyStyle();
- }
- });
- </script>
- </body>
- </html>
复制代码
5. 实战案例
5.1 实时表单验证
我们已经在前面的章节中展示了一个完整的表单验证示例。这个示例结合了正则表达式用于验证各种输入格式,以及DOM操作用于实时显示验证结果和错误消息。
5.2 动态搜索和过滤功能
同样,我们在前面也展示了一个实时搜索和过滤联系人列表的示例。这个示例使用了正则表达式来匹配搜索词,并通过DOM操作来动态显示或隐藏列表项,以及高亮显示匹配的文本。
5.3 内容高亮显示
让我们再看一个更复杂的内容高亮显示示例,它可以同时高亮多种不同类型的模式:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>多模式内容高亮</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 900px;
- margin: 0 auto;
- padding: 20px;
- }
- h1 {
- text-align: center;
- }
- .container {
- display: flex;
- flex-direction: column;
- gap: 20px;
- }
- .panel {
- border: 1px solid #ddd;
- border-radius: 5px;
- padding: 15px;
- }
- .panel-title {
- font-weight: bold;
- margin-bottom: 10px;
- font-size: 18px;
- }
- .highlight-controls {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
- gap: 15px;
- }
- .highlight-control {
- display: flex;
- flex-direction: column;
- gap: 5px;
- }
- .highlight-control label {
- display: flex;
- align-items: center;
- gap: 5px;
- font-weight: normal;
- }
- .highlight-control input[type="text"] {
- padding: 5px;
- border: 1px solid #ddd;
- border-radius: 3px;
- }
- .highlight-control select {
- padding: 5px;
- border: 1px solid #ddd;
- border-radius: 3px;
- }
- .color-picker {
- display: flex;
- gap: 5px;
- align-items: center;
- }
- .color-picker input[type="color"] {
- width: 30px;
- height: 30px;
- border: none;
- border-radius: 3px;
- cursor: pointer;
- }
- .content-area {
- min-height: 300px;
- padding: 15px;
- border: 1px solid #ddd;
- border-radius: 5px;
- line-height: 1.6;
- }
- .button-group {
- display: flex;
- gap: 10px;
- margin-top: 15px;
- }
- button {
- padding: 8px 15px;
- background-color: #4CAF50;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- }
- button:hover {
- background-color: #45a049;
- }
- button.secondary {
- background-color: #2196F3;
- }
- button.secondary:hover {
- background-color: #0b7dda;
- }
- .message {
- margin-top: 10px;
- padding: 10px;
- border-radius: 4px;
- display: none;
- }
- .success {
- background-color: #dff0d8;
- color: #3c763d;
- }
- .error {
- background-color: #f2dede;
- color: #a94442;
- }
- /* 高亮样式类 */
- .highlight-yellow {
- background-color: yellow;
- }
- .highlight-green {
- background-color: #90EE90;
- }
- .highlight-blue {
- background-color: #ADD8E6;
- }
- .highlight-red {
- background-color: #FFB6C1;
- }
- .highlight-purple {
- background-color: #D8BFD8;
- }
- .highlight-orange {
- background-color: #FFD700;
- }
- .highlight-custom {
- padding: 2px 4px;
- border-radius: 3px;
- }
- .highlight-underline {
- text-decoration: underline;
- text-decoration-style: wavy;
- text-decoration-color: red;
- }
- .highlight-bold {
- font-weight: bold;
- }
- .highlight-italic {
- font-style: italic;
- }
- </style>
- </head>
- <body>
- <h1>多模式内容高亮工具</h1>
-
- <div class="container">
- <div class="panel">
- <div class="panel-title">高亮规则设置</div>
- <div class="highlight-controls" id="highlightControls">
- <!-- 默认高亮规则 -->
- <div class="highlight-control">
- <label>
- <input type="checkbox" checked>
- <span>电子邮件</span>
- </label>
- <input type="text" value="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}" readonly>
- <select class="highlight-style">
- <option value="highlight-blue">蓝色高亮</option>
- <option value="highlight-green">绿色高亮</option>
- <option value="highlight-yellow">黄色高亮</option>
- <option value="highlight-red">红色高亮</option>
- <option value="highlight-purple">紫色高亮</option>
- <option value="highlight-orange">橙色高亮</option>
- <option value="highlight-underline">下划线</option>
- <option value="highlight-bold">粗体</option>
- <option value="highlight-italic">斜体</option>
- <option value="highlight-custom">自定义颜色</option>
- </select>
- <div class="color-picker" style="display: none;">
- <input type="color" value="#FFFF00">
- <span>背景色</span>
- </div>
- </div>
-
- <div class="highlight-control">
- <label>
- <input type="checkbox" checked>
- <span>电话号码</span>
- </label>
- <input type="text" value="(\d{3,4}-?)?\d{7,8}(?=-?\d{3,4})?|-?\d{3,4}-?\d{7,8}" readonly>
- <select class="highlight-style">
- <option value="highlight-blue">蓝色高亮</option>
- <option value="highlight-green" selected>绿色高亮</option>
- <option value="highlight-yellow">黄色高亮</option>
- <option value="highlight-red">红色高亮</option>
- <option value="highlight-purple">紫色高亮</option>
- <option value="highlight-orange">橙色高亮</option>
- <option value="highlight-underline">下划线</option>
- <option value="highlight-bold">粗体</option>
- <option value="highlight-italic">斜体</option>
- <option value="highlight-custom">自定义颜色</option>
- </select>
- <div class="color-picker" style="display: none;">
- <input type="color" value="#90EE90">
- <span>背景色</span>
- </div>
- </div>
-
- <div class="highlight-control">
- <label>
- <input type="checkbox" checked>
- <span>网址</span>
- </label>
- <input type="text" value="https?:\/\/[^\s]+" readonly>
- <select class="highlight-style">
- <option value="highlight-blue">蓝色高亮</option>
- <option value="highlight-green">绿色高亮</option>
- <option value="highlight-yellow">黄色高亮</option>
- <option value="highlight-red">红色高亮</option>
- <option value="highlight-purple">紫色高亮</option>
- <option value="highlight-orange" selected>橙色高亮</option>
- <option value="highlight-underline">下划线</option>
- <option value="highlight-bold">粗体</option>
- <option value="highlight-italic">斜体</option>
- <option value="highlight-custom">自定义颜色</option>
- </select>
- <div class="color-picker" style="display: none;">
- <input type="color" value="#FFD700">
- <span>背景色</span>
- </div>
- </div>
-
- <div class="highlight-control">
- <label>
- <input type="checkbox">
- <span>日期</span>
- </label>
- <input type="text" value="\d{4}-\d{2}-\d{2}|\d{2}/\d{2}/\d{4}">
- <select class="highlight-style">
- <option value="highlight-blue">蓝色高亮</option>
- <option value="highlight-green">绿色高亮</option>
- <option value="highlight-yellow">黄色高亮</option>
- <option value="highlight-red">红色高亮</option>
- <option value="highlight-purple" selected>紫色高亮</option>
- <option value="highlight-orange">橙色高亮</option>
- <option value="highlight-underline">下划线</option>
- <option value="highlight-bold">粗体</option>
- <option value="highlight-italic">斜体</option>
- <option value="highlight-custom">自定义颜色</option>
- </select>
- <div class="color-picker" style="display: none;">
- <input type="color" value="#D8BFD8">
- <span>背景色</span>
- </div>
- </div>
- </div>
-
- <div class="button-group">
- <button id="addRule">添加规则</button>
- <button id="applyHighlights" class="secondary">应用高亮</button>
- <button id="clearHighlights">清除高亮</button>
- </div>
-
- <div id="message" class="message"></div>
- </div>
-
- <div class="panel">
- <div class="panel-title">内容区域</div>
- <div class="content-area" id="contentArea">
- <h2>联系我们</h2>
- <p>如果您有任何问题或建议,请通过以下方式联系我们:</p>
- <p>电子邮件:contact@example.com 或 support@example.org</p>
- <p>电话:010-12345678 或 13800138000</p>
- <p>访问我们的网站:https://www.example.com 或 http://blog.example.org</p>
- <p>办公时间:周一至周五,9:00-18:00</p>
- <p>地址:北京市朝阳区某某街道123号</p>
- <p>邮政编码:100020</p>
-
- <h2>活动安排</h2>
- <p>2023-06-15:产品发布会</p>
- <p>2023/07/20:夏季促销活动</p>
- <p>2023-08-10:技术研讨会</p>
- <p>2023/09/05:秋季新品展示</p>
-
- <h2>价格信息</h2>
- <p>标准版:$99.00</p>
- <p>专业版:¥199.00</p>
- <p>企业版:$299.00</p>
-
- <h2>其他联系方式</h2>
- <p>QQ:123456789</p>
- <p>微信:example_company</p>
- <p>微博:@example公司</p>
- </div>
- </div>
- </div>
- <script>
- // 获取DOM元素
- const highlightControls = document.getElementById('highlightControls');
- const contentArea = document.getElementById('contentArea');
- const messageDiv = document.getElementById('message');
-
- // 获取按钮
- const addRuleBtn = document.getElementById('addRule');
- const applyHighlightsBtn = document.getElementById('applyHighlights');
- const clearHighlightsBtn = document.getElementById('clearHighlights');
-
- // 显示消息
- function showMessage(text, type) {
- messageDiv.textContent = text;
- messageDiv.className = 'message ' + type;
- messageDiv.style.display = 'block';
-
- // 3秒后隐藏消息
- setTimeout(() => {
- messageDiv.style.display = 'none';
- }, 3000);
- }
-
- // 添加高亮规则
- function addHighlightRule(name = '', pattern = '', enabled = true, style = 'highlight-yellow', color = '#FFFF00') {
- const ruleDiv = document.createElement('div');
- ruleDiv.className = 'highlight-control';
-
- ruleDiv.innerHTML = `
- <label>
- <input type="checkbox" ${enabled ? 'checked' : ''}>
- <span>${name || '自定义规则'}</span>
- </label>
- <input type="text" value="${pattern}" placeholder="输入正则表达式">
- <select class="highlight-style">
- <option value="highlight-blue" ${style === 'highlight-blue' ? 'selected' : ''}>蓝色高亮</option>
- <option value="highlight-green" ${style === 'highlight-green' ? 'selected' : ''}>绿色高亮</option>
- <option value="highlight-yellow" ${style === 'highlight-yellow' ? 'selected' : ''}>黄色高亮</option>
- <option value="highlight-red" ${style === 'highlight-red' ? 'selected' : ''}>红色高亮</option>
- <option value="highlight-purple" ${style === 'highlight-purple' ? 'selected' : ''}>紫色高亮</option>
- <option value="highlight-orange" ${style === 'highlight-orange' ? 'selected' : ''}>橙色高亮</option>
- <option value="highlight-underline" ${style === 'highlight-underline' ? 'selected' : ''}>下划线</option>
- <option value="highlight-bold" ${style === 'highlight-bold' ? 'selected' : ''}>粗体</option>
- <option value="highlight-italic" ${style === 'highlight-italic' ? 'selected' : ''}>斜体</option>
- <option value="highlight-custom" ${style === 'highlight-custom' ? 'selected' : ''}>自定义颜色</option>
- </select>
- <div class="color-picker" style="display: ${style === 'highlight-custom' ? 'flex' : 'none'};">
- <input type="color" value="${color}">
- <span>背景色</span>
- </div>
- <button class="remove-rule" style="margin-top: 5px; padding: 3px 8px; background-color: #f44336;">删除</button>
- `;
-
- highlightControls.appendChild(ruleDiv);
-
- // 添加事件监听器
- const styleSelect = ruleDiv.querySelector('.highlight-style');
- const colorPicker = ruleDiv.querySelector('.color-picker');
- const removeBtn = ruleDiv.querySelector('.remove-rule');
-
- styleSelect.addEventListener('change', function() {
- colorPicker.style.display = this.value === 'highlight-custom' ? 'flex' : 'none';
- });
-
- removeBtn.addEventListener('click', function() {
- ruleDiv.remove();
- });
- }
-
- // 应用高亮
- function applyHighlights() {
- // 清除现有高亮
- clearHighlights(false);
-
- // 获取所有规则
- const rules = [];
- const ruleElements = highlightControls.querySelectorAll('.highlight-control');
-
- ruleElements.forEach(ruleElement => {
- const enabled = ruleElement.querySelector('input[type="checkbox"]').checked;
- if (!enabled) return;
-
- const pattern = ruleElement.querySelector('input[type="text"]').value.trim();
- if (!pattern) return;
-
- const style = ruleElement.querySelector('.highlight-style').value;
- const color = style === 'highlight-custom' ?
- ruleElement.querySelector('.color-picker input[type="color"]').value :
- '';
-
- rules.push({
- pattern: pattern,
- style: style,
- color: color
- });
- });
-
- if (rules.length === 0) {
- showMessage('没有启用的规则', 'error');
- return;
- }
-
- try {
- // 获取内容区域的所有文本节点
- const walker = document.createTreeWalker(
- contentArea,
- NodeFilter.SHOW_TEXT,
- null,
- false
- );
-
- let node;
- const nodesToProcess = [];
-
- // 收集所有文本节点
- while (node = walker.nextNode()) {
- // 跳过脚本和样式元素内的文本
- if (node.parentNode.tagName !== 'SCRIPT' && node.parentNode.tagName !== 'STYLE') {
- nodesToProcess.push(node);
- }
- }
-
- // 处理每个文本节点
- nodesToProcess.forEach(textNode => {
- const text = textNode.textContent;
- let matches = [];
-
- // 收集所有匹配
- rules.forEach(rule => {
- try {
- const regex = new RegExp(rule.pattern, 'gi');
- let match;
- while ((match = regex.exec(text)) !== null) {
- matches.push({
- text: match[0],
- index: match.index,
- length: match[0].length,
- style: rule.style,
- color: rule.color
- });
- }
- } catch (error) {
- console.error('正则表达式错误:', error, '模式:', rule.pattern);
- }
- });
-
- // 按索引排序匹配
- matches.sort((a, b) => a.index - b.index);
-
- // 如果没有匹配,跳过此节点
- if (matches.length === 0) return;
-
- // 创建文档片段
- const fragment = document.createDocumentFragment();
- let lastIndex = 0;
-
- // 处理匹配
- matches.forEach(match => {
- // 添加匹配前的文本
- if (match.index > lastIndex) {
- fragment.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
- }
-
- // 创建匹配文本的span并应用样式
- const span = document.createElement('span');
- span.className = match.style;
- if (match.style === 'highlight-custom') {
- span.style.backgroundColor = match.color;
- }
- span.textContent = match.text;
- fragment.appendChild(span);
-
- lastIndex = match.index + match.length;
- });
-
- // 添加剩余的文本
- if (lastIndex < text.length) {
- fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
- }
-
- // 替换原始文本节点
- textNode.parentNode.replaceChild(fragment, textNode);
- });
-
- showMessage(`成功应用了 ${rules.length} 个高亮规则`, 'success');
- } catch (error) {
- showMessage('应用高亮时出错: ' + error.message, 'error');
- console.error('应用高亮时出错:', error);
- }
- }
-
- // 清除高亮
- function clearHighlights(showMessage = true) {
- // 获取所有带有高亮样式的span元素
- const highlightedSpans = contentArea.querySelectorAll('span[class*="highlight"]');
-
- highlightedSpans.forEach(span => {
- const parent = span.parentNode;
-
- // 用文本节点替换span元素
- parent.replaceChild(document.createTextNode(span.textContent), span);
-
- // 合并相邻的文本节点
- parent.normalize();
- });
-
- if (showMessage) {
- showMessage('已清除所有高亮', 'success');
- }
- }
-
- // 添加事件监听器
- addRuleBtn.addEventListener('click', function() {
- addHighlightRule();
- });
-
- applyHighlightsBtn.addEventListener('click', applyHighlights);
- clearHighlightsBtn.addEventListener('click', () => clearHighlights(true));
-
- // 为现有的样式选择器添加事件监听器
- document.querySelectorAll('.highlight-control').forEach(control => {
- const styleSelect = control.querySelector('.highlight-style');
- const colorPicker = control.querySelector('.color-picker');
-
- if (styleSelect && colorPicker) {
- styleSelect.addEventListener('change', function() {
- colorPicker.style.display = this.value === 'highlight-custom' ? 'flex' : 'none';
- });
- }
-
- const removeBtn = control.querySelector('.remove-rule');
- if (removeBtn) {
- removeBtn.addEventListener('click', function() {
- control.remove();
- });
- }
- });
- </script>
- </body>
- </html>
复制代码
5.4 数据格式化和展示
以下是一个数据格式化和展示的示例,它使用正则表达式来解析和格式化不同类型的数据:
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>数据格式化和展示</title>
- <style>
- body {
- font-family: Arial, sans-serif;
- max-width: 1000px;
- margin: 0 auto;
- padding: 20px;
- }
- h1 {
- text-align: center;
- }
- .container {
- display: flex;
- flex-direction: column;
- gap: 20px;
- }
- .panel {
- border: 1px solid #ddd;
- border-radius: 5px;
- padding: 15px;
- }
- .panel-title {
- font-weight: bold;
- margin-bottom: 10px;
- font-size: 18px;
- }
- .input-group {
- margin-bottom: 15px;
- }
- .input-group label {
- display: block;
- margin-bottom: 5px;
- font-weight: bold;
- }
- textarea {
- width: 100%;
- height: 150px;
- padding: 10px;
- border: 1px solid #ddd;
- border-radius: 4px;
- resize: vertical;
- font-family: inherit;
- }
- .button-group {
- display: flex;
- gap: 10px;
- margin-top: 10px;
- }
- button {
- padding: 8px 15px;
- background-color: #4CAF50;
- color: white;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- }
- button:hover {
- background-color: #45a049;
- }
- button.secondary {
- background-color: #2196F3;
- }
- button.secondary:hover {
- background-color: #0b7dda;
- }
- .tabs {
- display: flex;
- border-bottom: 1px solid #ddd;
- margin-bottom: 15px;
- }
- .tab {
- padding: 10px 15px;
- cursor: pointer;
- border: 1px solid transparent;
- border-bottom: none;
- border-radius: 4px 4px 0 0;
- margin-right: 5px;
- background-color: #f1f1f1;
- }
- .tab.active {
- background-color: white;
- border-color: #ddd;
- border-bottom-color: white;
- margin-bottom: -1px;
- font-weight: bold;
- }
- .tab-content {
- display: none;
- }
- .tab-content.active {
- display: block;
- }
- table {
- width: 100%;
- border-collapse: collapse;
- margin-top: 10px;
- }
- table, th, td {
- border: 1px solid #ddd;
- }
- th, td {
- padding: 8px;
- text-align: left;
- }
- th {
- background-color: #f2f2f2;
- }
- tr:nth-child(even) {
- background-color: #f9f9f9;
- }
- .data-card {
- border: 1px solid #ddd;
- border-radius: 5px;
- padding: 15px;
- margin-bottom: 15px;
- background-color: #f9f9f9;
- }
- .data-card h3 {
- margin-top: 0;
- color: #333;
- }
- .data-card .data-item {
- margin-bottom: 8px;
- }
- .data-card .data-label {
- font-weight: bold;
- display: inline-block;
- width: 120px;
- }
- .message {
- margin-top: 10px;
- padding: 10px;
- border-radius: 4px;
- display: none;
- }
- .success {
- background-color: #dff0d8;
- color: #3c763d;
- }
- .error {
- background-color: #f2dede;
- color: #a94442;
- }
- .no-data {
- text-align: center;
- padding: 20px;
- color: #666;
- }
- </style>
- </head>
- <body>
- <h1>数据格式化和展示工具</h1>
-
- <div class="container">
- <div class="panel">
- <div class="panel-title">输入数据</div>
- <div class="input-group">
- <label for="inputData">输入原始数据 (支持多种格式):</label>
- <textarea id="inputData" placeholder="在此输入要格式化的数据,例如:
- 姓名: 张三, 年龄: 30, 邮箱: zhangsan@example.com, 电话: 13800138000
- 姓名: 李四, 年龄: 25, 邮箱: lisi@example.com, 电话: 13900139000
- 姓名: 王五, 年龄: 35, 邮箱: wangwu@example.com, 电话: 13700137000"></textarea>
- </div>
- <div class="button-group">
- <button id="parseData">解析数据</button>
- <button id="clearData" class="secondary">清除数据</button>
- <button id="loadSample" class="secondary">加载示例数据</button>
- </div>
- <div id="message" class="message"></div>
- </div>
-
- <div class="panel">
- <div class="panel-title">格式化结果</div>
- <div class="tabs">
- <div class="tab active" data-tab="table">表格视图</div>
- <div class="tab" data-tab="cards">卡片视图</div>
- <div class="tab" data-tab="json">JSON视图</div>
- </div>
-
- <div id="tableTab" class="tab-content active">
- <div id="tableContainer">
- <div class="no-data">暂无数据,请先输入并解析数据</div>
- </div>
- </div>
-
- <div id="cardsTab" class="tab-content">
- <div id="cardsContainer">
- <div class="no-data">暂无数据,请先输入并解析数据</div>
- </div>
- </div>
-
- <div id="jsonTab" class="tab-content">
- <div class="input-group">
- <label for="jsonOutput">JSON格式数据:</label>
- <textarea id="jsonOutput" readonly placeholder="JSON格式的数据将显示在这里..."></textarea>
- </div>
- </div>
- </div>
- </div>
- <script>
- // 获取DOM元素
- const inputData = document.getElementById('inputData');
- const messageDiv = document.getElementById('message');
- const tableContainer = document.getElementById('tableContainer');
- const cardsContainer = document.getElementById('cardsContainer');
- const jsonOutput = document.getElementById('jsonOutput');
-
- // 获取按钮
- const parseDataBtn = document.getElementById('parseData');
- const clearDataBtn = document.getElementById('clearData');
- const loadSampleBtn = document.getElementById('loadSample');
-
- // 获取标签页
- const tabs = document.querySelectorAll('.tab');
- const tabContents = document.querySelectorAll('.tab-content');
-
- // 存储解析后的数据
- let parsedData = [];
-
- // 显示消息
- function showMessage(text, type) {
- messageDiv.textContent = text;
- messageDiv.className = 'message ' + type;
- messageDiv.style.display = 'block';
-
- // 3秒后隐藏消息
- setTimeout(() => {
- messageDiv.style.display = 'none';
- }, 3000);
- }
-
- // 标签页切换
- tabs.forEach(tab => {
- tab.addEventListener('click', function() {
- const tabId = this.getAttribute('data-tab');
-
- // 移除所有活动状态
- tabs.forEach(t => t.classList.remove('active'));
- tabContents.forEach(c => c.classList.remove('active'));
-
- // 添加当前活动状态
- this.classList.add('active');
- document.getElementById(tabId + 'Tab').classList.add('active');
- });
- });
-
- // 解析数据
- function parseData() {
- const input = inputData.value.trim();
- if (!input) {
- showMessage('请输入数据', 'error');
- return;
- }
-
- try {
- // 尝试解析不同格式的数据
- parsedData = [];
-
- // 尝试解析键值对格式 (例如: 姓名: 张三, 年龄: 30)
- if (input.includes(':') && (input.includes(',') || input.includes('\n'))) {
- parseKeyValueFormat(input);
- }
- // 尝试解析CSV格式
- else if (input.includes(',')) {
- parseCSVFormat(input);
- }
- // 尝试解析JSON格式
- else if (input.startsWith('[') || input.startsWith('{')) {
- parseJSONFormat(input);
- }
- // 尝试解析每行一个记录的格式
- else if (input.includes('\n')) {
- parseLineFormat(input);
- }
- else {
- showMessage('无法识别的数据格式', 'error');
- return;
- }
-
- if (parsedData.length === 0) {
- showMessage('未能解析出有效数据', 'error');
- return;
- }
-
- // 显示解析结果
- displayData();
- showMessage(`成功解析了 ${parsedData.length} 条记录`, 'success');
-
- } catch (error) {
- showMessage('解析数据时出错: ' + error.message, 'error');
- console.error('解析数据时出错:', error);
- }
- }
-
- // 解析键值对格式
- function parseKeyValueFormat(input) {
- const lines = input.split('\n');
- let currentRecord = {};
-
- lines.forEach(line => {
- line = line.trim();
- if (!line) return;
-
- // 使用正则表达式匹配键值对
- const keyValuePairs = line.match(/([^:,\s]+)\s*:\s*([^,]+)/g);
-
- if (keyValuePairs) {
- // 如果是新记录,保存上一条记录
- if (Object.keys(currentRecord).length > 0) {
- parsedData.push(currentRecord);
- currentRecord = {};
- }
-
- // 解析键值对
- keyValuePairs.forEach(pair => {
- const match = pair.match(/([^:,\s]+)\s*:\s*([^,]+)/);
- if (match) {
- const key = match[1].trim();
- const value = match[2].trim();
-
- // 尝试转换数值
- if (/^\d+$/.test(value)) {
- currentRecord[key] = parseInt(value, 10);
- } else if (/^\d+\.\d+$/.test(value)) {
- currentRecord[key] = parseFloat(value);
- } else {
- currentRecord[key] = value;
- }
- }
- });
- }
- });
-
- // 添加最后一条记录
- if (Object.keys(currentRecord).length > 0) {
- parsedData.push(currentRecord);
- }
- }
-
- // 解析CSV格式
- function parseCSVFormat(input) {
- const lines = input.split('\n');
- if (lines.length < 2) return;
-
- // 解析标题行
- const headers = lines[0].split(',').map(h => h.trim());
-
- // 解析数据行
- for (let i = 1; i < lines.length; i++) {
- const line = lines[i].trim();
- if (!line) continue;
-
- const values = line.split(',').map(v => v.trim());
- if (values.length !== headers.length) continue;
-
- const record = {};
- headers.forEach((header, index) => {
- const value = values[index];
-
- // 尝试转换数值
- if (/^\d+$/.test(value)) {
- record[header] = parseInt(value, 10);
- } else if (/^\d+\.\d+$/.test(value)) {
- record[header] = parseFloat(value);
- } else {
- record[header] = value;
- }
- });
-
- parsedData.push(record);
- }
- }
-
- // 解析JSON格式
- function parseJSONFormat(input) {
- const data = JSON.parse(input);
-
- if (Array.isArray(data)) {
- parsedData = data;
- } else if (typeof data === 'object') {
- parsedData = [data];
- }
- }
-
- // 解析每行一个记录的格式
- function parseLineFormat(input) {
- const lines = input.split('\n');
-
- lines.forEach(line => {
- line = line.trim();
- if (!line) return;
-
- // 尝试提取常见数据类型
- const record = {};
-
- // 提取电子邮件
- const emailMatch = line.match(/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/);
- if (emailMatch) {
- record['邮箱'] = emailMatch[0];
- }
-
- // 提取电话号码
- const phoneMatch = line.match(/(\d{3,4}-?)?\d{7,8}(?=-?\d{3,4})?|-?\d{3,4}-?\d{7,8}/);
- if (phoneMatch) {
- record['电话'] = phoneMatch[0];
- }
-
- // 提取日期
- const dateMatch = line.match(/(\d{4}-\d{2}-\d{2}|\d{2}\/\d{2}\/\d{4})/);
- if (dateMatch) {
- record['日期'] = dateMatch[0];
- }
-
- // 提取URL
- const urlMatch = line.match(/(https?:\/\/[^\s]+)/);
- if (urlMatch) {
- record['网址'] = urlMatch[0];
- }
-
- // 提取数字
- const numberMatches = line.match(/\d+\.?\d*/g);
- if (numberMatches) {
- numberMatches.forEach((num, index) => {
- const key = index === 0 ? '数值' : `数值${index + 1}`;
- if (num.includes('.')) {
- record[key] = parseFloat(num);
- } else {
- record[key] = parseInt(num, 10);
- }
- });
- }
-
- // 如果没有提取到任何数据,将整行作为内容
- if (Object.keys(record).length === 0) {
- record['内容'] = line;
- }
-
- parsedData.push(record);
- });
- }
-
- // 显示数据
- function displayData() {
- // 显示表格视图
- displayTableView();
-
- // 显示卡片视图
- displayCardsView();
-
- // 显示JSON视图
- displayJSONView();
- }
-
- // 显示表格视图
- function displayTableView() {
- if (parsedData.length === 0) {
- tableContainer.innerHTML = '<div class="no-data">暂无数据</div>';
- return;
- }
-
- // 获取所有可能的列
- const columns = new Set();
- parsedData.forEach(record => {
- Object.keys(record).forEach(key => columns.add(key));
- });
-
- // 创建表格
- let tableHTML = '<table><thead><tr>';
- Array.from(columns).forEach(column => {
- tableHTML += `<th>${column}</th>`;
- });
- tableHTML += '</tr></thead><tbody>';
-
- // 添加数据行
- parsedData.forEach(record => {
- tableHTML += '<tr>';
- Array.from(columns).forEach(column => {
- const value = record[column] !== undefined ? record[column] : '';
- tableHTML += `<td>${value}</td>`;
- });
- tableHTML += '</tr>';
- });
-
- tableHTML += '</tbody></table>';
- tableContainer.innerHTML = tableHTML;
- }
-
- // 显示卡片视图
- function displayCardsView() {
- if (parsedData.length === 0) {
- cardsContainer.innerHTML = '<div class="no-data">暂无数据</div>';
- return;
- }
-
- let cardsHTML = '';
-
- parsedData.forEach((record, index) => {
- cardsHTML += '<div class="data-card">';
- cardsHTML += `<h3>记录 ${index + 1}</h3>`;
-
- Object.keys(record).forEach(key => {
- cardsHTML += `<div class="data-item">`;
- cardsHTML += `<span class="data-label">${key}:</span>`;
- cardsHTML += `<span>${record[key]}</span>`;
- cardsHTML += `</div>`;
- });
-
- cardsHTML += '</div>';
- });
-
- cardsContainer.innerHTML = cardsHTML;
- }
-
- // 显示JSON视图
- function displayJSONView() {
- if (parsedData.length === 0) {
- jsonOutput.value = '';
- return;
- }
-
- try {
- const jsonString = JSON.stringify(parsedData, null, 2);
- jsonOutput.value = jsonString;
- } catch (error) {
- jsonOutput.value = 'JSON序列化错误: ' + error.message;
- }
- }
-
- // 清除数据
- function clearData() {
- inputData.value = '';
- parsedData = [];
- tableContainer.innerHTML = '<div class="no-data">暂无数据,请先输入并解析数据</div>';
- cardsContainer.innerHTML = '<div class="no-data">暂无数据,请先输入并解析数据</div>';
- jsonOutput.value = '';
- showMessage('数据已清除', 'success');
- }
-
- // 加载示例数据
- function loadSampleData() {
- const sampleData = `姓名: 张三, 年龄: 30, 邮箱: zhangsan@example.com, 电话: 13800138000, 部门: 技术部
- 姓名: 李四, 年龄: 25, 邮箱: lisi@example.com, 电话: 13900139000, 部门: 市场部
- 姓名: 王五, 年龄: 35, 邮箱: wangwu@example.com, 电话: 13700137000, 部门: 人事部
- 姓名: 赵六, 年龄: 28, 邮箱: zhaoliu@example.com, 电话: 13600136000, 部门: 财务部
- 姓名: 钱七, 年龄: 32, 邮箱: qianqi@example.com, 电话: 13500135000, 部门: 技术部`;
-
- inputData.value = sampleData;
- showMessage('示例数据已加载', 'success');
- }
-
- // 添加事件监听器
- parseDataBtn.addEventListener('click', parseData);
- clearDataBtn.addEventListener('click', clearData);
- loadSampleBtn.addEventListener('click', loadSampleData);
- </script>
- </body>
- </html>
复制代码
6. 性能优化和最佳实践
6.1 正则表达式性能优化
正则表达式虽然强大,但如果使用不当可能会导致性能问题。以下是一些优化正则表达式性能的最佳实践:
贪婪匹配(使用*和+量词)会尝试匹配尽可能多的字符,这可能导致大量的回溯操作。在可能的情况下,使用惰性匹配(*?和+?)或更精确的匹配模式。
- // 不推荐的贪婪匹配
- const html = '<div>内容1</div><div>内容2</div>';
- const matches = html.match(/<div>.*<\/div>/g); // 匹配整个字符串
- // 推荐的惰性匹配
- const matches = html.match(/<div>.*?<\/div>/g); // 分别匹配两个div
复制代码
字符类(如[abc])通常比选择(如(a|b|c))更高效。
- // 不推荐的选择
- const pattern = /(a|b|c)/;
- // 推荐的字符类
- const pattern = /[abc]/;
复制代码
捕获组会消耗额外的内存和处理时间。如果不需要引用匹配的组,使用非捕获组(?:...)。
- // 不推荐的捕获组
- const pattern = /(\d{4})-(\d{2})-(\d{2})/;
- // 推荐的非捕获组(如果不需要引用)
- const pattern = /(?:\d{4})-(?:\d{2})-(?:\d{2})/;
复制代码
如果可能,使用锚点(如^和$)来限制匹配范围,这样可以减少不必要的匹配尝试。
- // 不推荐的无锚点匹配
- const pattern = /error/;
- // 推荐的有锚点匹配(如果知道错误在行首)
- const pattern = /^error/;
复制代码
如果同一个正则表达式会被多次使用,预编译它可以提高性能。
- // 不推荐的每次创建新正则表达式
- function validateEmail(email) {
- return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
- }
- // 推荐的预编译正则表达式
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- function validateEmail(email) {
- return emailRegex.test(email);
- }
复制代码
6.2 DOM操作性能优化
DOM操作是前端开发中的常见性能瓶颈。以下是一些优化DOM操作性能的最佳实践:
DOM访问是昂贵的操作,应该尽量减少。缓存DOM查询结果,避免重复查询。
- // 不推荐的重复DOM查询
- function updateItems() {
- const items = document.querySelectorAll('.item');
- items.forEach(item => {
- item.style.color = 'red';
- });
-
- // 其他操作...
-
- // 再次查询相同的元素
- const items2 = document.querySelectorAll('.item');
- items2.forEach(item => {
- item.style.backgroundColor = 'yellow';
- });
- }
- // 推荐的缓存DOM查询结果
- function updateItems() {
- const items = document.querySelectorAll('.item');
-
- items.forEach(item => {
- item.style.color = 'red';
- });
-
- // 其他操作...
-
- // 使用缓存的元素
- items.forEach(item => {
- item.style.backgroundColor = 'yellow';
- });
- }
复制代码
当需要添加多个DOM元素时,使用文档片段(DocumentFragment)可以减少重排和重绘。
- // 不推荐的直接添加多个元素
- function addItems(items) {
- const container = document.getElementById('container');
- items.forEach(item => {
- const div = document.createElement('div');
- div.textContent = item;
- container.appendChild(div);
- });
- }
- // 推荐的使用文档片段
- function addItems(items) {
- const container = document.getElementById('container');
- const fragment = document.createDocumentFragment();
-
- items.forEach(item => {
- const div = document.createElement('div');
- div.textContent = item;
- fragment.appendChild(div);
- });
-
- container.appendChild(fragment);
- }
复制代码
避免频繁修改单个元素的样式,而是通过修改类名来批量更新样式。
- // 不推荐的逐个修改样式
- function highlightItems() {
- const items = document.querySelectorAll('.item');
- items.forEach(item => {
- item.style.color = 'red';
- item.style.backgroundColor = 'yellow';
- item.style.fontWeight = 'bold';
- });
- }
- // 推荐的通过类名批量更新样式
- function highlightItems() {
- const items = document.querySelectorAll('.item');
- items.forEach(item => {
- item.classList.add('highlight');
- });
- }
- // CSS中定义.highlight类
- // .highlight {
- // color: red;
- // background-color: yellow;
- // font-weight: bold;
- // }
复制代码
当需要为多个元素添加相同的事件处理程序时,使用事件委托可以减少内存使用和提高性能。
- // 不推荐的为每个元素添加事件监听器
- function setupItemHandlers() {
- const items = document.querySelectorAll('.item');
- items.forEach(item => {
- item.addEventListener('click', function() {
- console.log('Item clicked:', this.textContent);
- });
- });
- }
- // 推荐的事件委托
- function setupItemHandlers() {
- const container = document.getElementById('container');
- container.addEventListener('click', function(event) {
- if (event.target.classList.contains('item')) {
- console.log('Item clicked:', event.target.textContent);
- }
- });
- }
复制代码
避免在JavaScript中读取布局信息后立即修改布局,这会导致强制同步布局,影响性能。
- // 不推荐的强制同步布局
- function updateElement() {
- const element = document.getElementById('myElement');
- // 读取布局信息
- const width = element.offsetWidth;
- // 立即修改布局
- element.style.height = width * 2 + 'px';
- }
- // 推荐的分离读写操作
- function updateElement() {
- const element = document.getElementById('myElement');
- // 先读取所有布局信息
- const width = element.offsetWidth;
- const height = element.offsetHeight;
-
- // 然后进行所有布局修改
- element.style.height = width * 2 + 'px';
- element.style.width = height * 2 + 'px';
- }
复制代码
6.3 代码组织和可维护性
良好的代码组织和可维护性是长期项目成功的关键。以下是一些最佳实践:
将代码组织成模块,每个模块负责特定的功能。
- // regexUtils.js - 正则表达式工具模块
- export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- export const phoneRegex = /^1[3-9]\d{9}$/;
- export function validateEmail(email) {
- return emailRegex.test(email);
- }
- export function validatePhone(phone) {
- return phoneRegex.test(phone);
- }
- // domUtils.js - DOM操作工具模块
- export function createElement(tag, attributes, content) {
- const element = document.createElement(tag);
-
- if (attributes) {
- Object.keys(attributes).forEach(key => {
- element.setAttribute(key, attributes[key]);
- });
- }
-
- if (content) {
- element.textContent = content;
- }
-
- return element;
- }
- export function addClass(element, className) {
- element.classList.add(className);
- }
- // main.js - 主模块
- import { validateEmail, validatePhone } from './regexUtils.js';
- import { createElement, addClass } from './domUtils.js';
- function processForm(formData) {
- if (!validateEmail(formData.email)) {
- showError('邮箱格式不正确');
- return false;
- }
-
- if (!validatePhone(formData.phone)) {
- showError('手机号格式不正确');
- return false;
- }
-
- // 处理表单数据...
- return true;
- }
复制代码
使用适当的设计模式可以提高代码的可维护性和可扩展性。
- // 策略模式 - 用于不同的验证策略
- class Validator {
- constructor(strategy) {
- this.strategy = strategy;
- }
-
- validate(value) {
- return this.strategy.validate(value);
- }
- }
- class EmailValidator {
- validate(email) {
- return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email);
- }
- }
- class PhoneValidator {
- validate(phone) {
- return /^1[3-9]\d{9}$/.test(phone);
- }
- }
- // 使用策略模式
- const emailValidator = new Validator(new EmailValidator());
- const phoneValidator = new Validator(new PhoneValidator());
- console.log(emailValidator.validate('test@example.com')); // true
- console.log(phoneValidator.validate('13800138000')); // true
复制代码
编写可测试的代码可以提高代码质量和可维护性。
- // 可测试的验证函数
- function validateInput(input, pattern) {
- if (!input || !pattern) {
- return { isValid: false, error: '输入和模式不能为空' };
- }
-
- try {
- const regex = new RegExp(pattern);
- const isValid = regex.test(input);
- return { isValid, error: isValid ? '' : '输入格式不正确' };
- } catch (error) {
- return { isValid: false, error: '正则表达式错误: ' + error.message };
- }
- }
- // 测试用例
- function testValidateInput() {
- // 测试邮箱验证
- const emailResult = validateInput('test@example.com', '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$');
- console.assert(emailResult.isValid === true, '邮箱验证测试失败');
-
- // 测试无效邮箱
- const invalidEmailResult = validateInput('invalid-email', '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$');
- console.assert(invalidEmailResult.isValid === false, '无效邮箱验证测试失败');
-
- // 测试空输入
- const emptyResult = validateInput('', '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$');
- console.assert(emptyResult.isValid === false, '空输入验证测试失败');
-
- console.log('所有测试通过');
- }
- testValidateInput();
复制代码
良好的注释和文档可以提高代码的可读性和可维护性。
- /**
- * 验证邮箱地址格式
- * @param {string} email - 要验证的邮箱地址
- * @returns {boolean} - 如果邮箱格式正确返回true,否则返回false
- *
- * @example
- * // 返回 true
- * validateEmail('test@example.com');
- *
- * @example
- * // 返回 false
- * validateEmail('invalid-email');
- */
- function validateEmail(email) {
- if (!email || typeof email !== 'string') {
- return false;
- }
-
- // 邮箱正则表达式,支持大多数常见邮箱格式
- const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
- return emailRegex.test(email);
- }
复制代码
7. 结论
正则表达式和JavaScript DOM操作是前端开发中的两项核心技术,它们结合使用可以创建高效、动态且用户友好的网页。通过本文的学习,我们了解了:
1. 正则表达式的基础知识和常用模式,以及如何在JavaScript中使用它们进行文本匹配和处理。
2. JavaScript DOM操作的基本方法,包括选择、修改、创建和删除DOM元素,以及事件处理。
3. 如何结合正则表达式和DOM操作实现表单验证、动态搜索和过滤、文本处理和格式化、动态样式应用等功能。
4. 通过实战案例,我们看到了这些技术在实际应用中的强大功能。
5. 性能优化和最佳实践,帮助我们编写更高效、更可维护的代码。
掌握这些技能将大大提升你的前端开发能力,使你能够创建更加动态、交互性更强的网页应用。继续实践和探索,你会发现正则表达式和DOM操作的更多可能性,为你的项目带来更大的价值。
7.1 进一步学习的资源
如果你想进一步深入学习正则表达式和JavaScript DOM操作,以下资源可能会有所帮助:
1. MDN Web文档:正则表达式文档对象模型 (DOM)
2. 正则表达式
3. 文档对象模型 (DOM)
4. 书籍:《JavaScript高级程序设计》(第4版)- Nicholas C. Zakas《JavaScript权威指南》(第7版)- David Flanagan《正则表达式必知必会》- Ben Forta
5. 《JavaScript高级程序设计》(第4版)- Nicholas C. Zakas
6. 《JavaScript权威指南》(第7版)- David Flanagan
7. 《正则表达式必知必会》- Ben Forta
8. 在线工具:RegExr- 正则表达式学习和测试工具Regex101- 正则表达式测试和调试工具JSFiddle- 在线JavaScript代码编辑和测试
9. RegExr- 正则表达式学习和测试工具
10. Regex101- 正则表达式测试和调试工具
11. JSFiddle- 在线JavaScript代码编辑和测试
12. 课程:Coursera上的”JavaScript, jQuery, and JSON”课程Udemy上的”JavaScript: Understanding the Weird Parts”课程Frontend Masters上的”JavaScript: The Hard Parts”课程
13. Coursera上的”JavaScript, jQuery, and JSON”课程
14. Udemy上的”JavaScript: Understanding the Weird Parts”课程
15. Frontend Masters上的”JavaScript: The Hard Parts”课程
MDN Web文档:
• 正则表达式
• 文档对象模型 (DOM)
书籍:
• 《JavaScript高级程序设计》(第4版)- Nicholas C. Zakas
• 《JavaScript权威指南》(第7版)- David Flanagan
• 《正则表达式必知必会》- Ben Forta
在线工具:
• RegExr- 正则表达式学习和测试工具
• Regex101- 正则表达式测试和调试工具
• JSFiddle- 在线JavaScript代码编辑和测试
课程:
• Coursera上的”JavaScript, jQuery, and JSON”课程
• Udemy上的”JavaScript: Understanding the Weird Parts”课程
• Frontend Masters上的”JavaScript: The Hard Parts”课程
通过不断学习和实践,你将能够更加熟练地运用正则表达式和DOM操作,创建出更加出色的前端应用。 |
|