JS怎么实现点击目录名变换颜色的效果

发布时间:2021-07-21 10:50:09 作者:chen
来源:亿速云 阅读:148
# JS怎么实现点击目录名变换颜色的效果

## 前言

在网页开发中,目录导航是提升用户体验的重要组件。一个常见的交互需求是:当用户点击目录项时,该项颜色发生变化以指示当前选中状态。本文将详细介绍如何使用原生JavaScript实现这一效果,涵盖从基础实现到进阶优化的完整方案。

## 一、基础HTML结构准备

首先需要构建基本的HTML目录结构:

```html
<div class="catalog">
  <h2>文章目录</h2>
  <ul id="menu">
    <li class="menu-item">第一章 基础知识</li>
    <li class="menu-item">第二章 进阶技巧</li>
    <li class="menu-item">第三章 实战应用</li>
    <li class="menu-item">第四章 性能优化</li>
  </ul>
</div>

二、CSS样式设置

为目录项添加基础样式和激活状态样式:

.menu-item {
  padding: 8px 12px;
  cursor: pointer;
  transition: all 0.3s ease;
  list-style-type: none;
  margin: 5px 0;
  border-radius: 4px;
}

.menu-item:hover {
  background-color: #f0f0f0;
}

.menu-item.active {
  color: #ffffff;
  background-color: #4285f4;
  font-weight: bold;
}

三、JavaScript基础实现

3.1 获取DOM元素

const menuItems = document.querySelectorAll('.menu-item');

3.2 添加点击事件监听

menuItems.forEach(item => {
  item.addEventListener('click', function() {
    // 移除所有项目的active类
    menuItems.forEach(i => i.classList.remove('active'));
    
    // 为当前点击项添加active类
    this.classList.add('active');
  });
});

四、功能扩展与优化

4.1 保存选中状态

使用localStorage保存用户选择:

menuItems.forEach(item => {
  item.addEventListener('click', function() {
    menuItems.forEach(i => i.classList.remove('active'));
    this.classList.add('active');
    
    // 保存到本地存储
    localStorage.setItem('selectedMenuItem', this.textContent);
  });
});

// 页面加载时恢复选择
window.addEventListener('DOMContentLoaded', () => {
  const selected = localStorage.getItem('selectedMenuItem');
  if (selected) {
    menuItems.forEach(item => {
      if (item.textContent === selected) {
        item.classList.add('active');
      }
    });
  }
});

4.2 动画效果增强

添加更平滑的过渡效果:

.menu-item {
  transition: color 0.2s, background-color 0.3s, transform 0.1s;
}

.menu-item:active {
  transform: scale(0.98);
}

4.3 键盘导航支持

document.addEventListener('keydown', (e) => {
  const activeItem = document.querySelector('.menu-item.active');
  let index = Array.from(menuItems).indexOf(activeItem);
  
  if (e.key === 'ArrowDown') {
    index = (index + 1) % menuItems.length;
  } else if (e.key === 'ArrowUp') {
    index = (index - 1 + menuItems.length) % menuItems.length;
  } else if (e.key === 'Enter' && activeItem) {
    activeItem.click();
    return;
  }
  
  menuItems.forEach(i => i.classList.remove('active'));
  menuItems[index]?.classList.add('active');
});

五、封装为可复用组件

将功能封装为类:

class InteractiveMenu {
  constructor(selector) {
    this.menu = document.querySelector(selector);
    this.items = this.menu.querySelectorAll('.menu-item');
    this.init();
  }
  
  init() {
    this.items.forEach(item => {
      item.addEventListener('click', () => this.selectItem(item));
    });
    
    this.restoreSelection();
  }
  
  selectItem(selectedItem) {
    this.items.forEach(item => item.classList.remove('active'));
    selectedItem.classList.add('active');
    this.saveSelection(selectedItem);
  }
  
  saveSelection(item) {
    localStorage.setItem('selectedMenuItem', item.textContent);
  }
  
  restoreSelection() {
    const selected = localStorage.getItem('selectedMenuItem');
    if (selected) {
      this.items.forEach(item => {
        if (item.textContent === selected) {
          item.classList.add('active');
        }
      });
    }
  }
}

// 使用示例
new InteractiveMenu('#menu');

六、性能优化建议

  1. 事件委托:对于大型目录,使用事件委托提高性能
document.getElementById('menu').addEventListener('click', (e) => {
  if (e.target.classList.contains('menu-item')) {
    menuItems.forEach(i => i.classList.remove('active'));
    e.target.classList.add('active');
  }
});
  1. 防抖处理:防止快速连续点击
function debounce(func, delay) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, arguments), delay);
  };
}

menuItems.forEach(item => {
  item.addEventListener('click', debounce(function() {
    menuItems.forEach(i => i.classList.remove('active'));
    this.classList.add('active');
  }, 100));
});

七、兼容性处理

  1. classList兼容
// 对于不支持classList的旧浏览器
function toggleClass(element, className) {
  if (element.classList) {
    element.classList.toggle(className);
  } else {
    const classes = element.className.split(' ');
    const index = classes.indexOf(className);
    
    if (index >= 0) {
      classes.splice(index, 1);
    } else {
      classes.push(className);
    }
    
    element.className = classes.join(' ');
  }
}
  1. ES6语法转换:使用Babel转换代码以支持旧浏览器

八、完整示例代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>交互式目录示例</title>
  <style>
    .menu-item {
      padding: 8px 12px;
      cursor: pointer;
      transition: all 0.3s ease;
      list-style-type: none;
      margin: 5px 0;
      border-radius: 4px;
    }
    .menu-item:hover {
      background-color: #f0f0f0;
    }
    .menu-item.active {
      color: #ffffff;
      background-color: #4285f4;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <div class="catalog">
    <h2>文章目录</h2>
    <ul id="menu">
      <li class="menu-item">第一章 基础知识</li>
      <li class="menu-item">第二章 进阶技巧</li>
      <li class="menu-item">第三章 实战应用</li>
      <li class="menu-item">第四章 性能优化</li>
    </ul>
  </div>

  <script>
    class InteractiveMenu {
      constructor(selector) {
        this.menu = document.querySelector(selector);
        this.items = this.menu.querySelectorAll('.menu-item');
        this.init();
      }
      
      init() {
        this.menu.addEventListener('click', (e) => {
          if (e.target.classList.contains('menu-item')) {
            this.selectItem(e.target);
          }
        });
        
        document.addEventListener('keydown', (e) => {
          const activeItem = this.menu.querySelector('.menu-item.active');
          let index = Array.from(this.items).indexOf(activeItem);
          
          if (e.key === 'ArrowDown') {
            index = (index + 1) % this.items.length;
          } else if (e.key === 'ArrowUp') {
            index = (index - 1 + this.items.length) % this.items.length;
          } else if (e.key === 'Enter' && activeItem) {
            activeItem.click();
            return;
          }
          
          this.selectItem(this.items[index]);
        });
        
        this.restoreSelection();
      }
      
      selectItem(selectedItem) {
        if (!selectedItem) return;
        
        this.items.forEach(item => item.classList.remove('active'));
        selectedItem.classList.add('active');
        this.saveSelection(selectedItem);
      }
      
      saveSelection(item) {
        localStorage.setItem('selectedMenuItem', item.textContent);
      }
      
      restoreSelection() {
        const selected = localStorage.getItem('selectedMenuItem');
        if (selected) {
          this.items.forEach(item => {
            if (item.textContent === selected) {
              item.classList.add('active');
            }
          });
        }
      }
    }

    // 初始化菜单
    new InteractiveMenu('#menu');
  </script>
</body>
</html>

九、总结

本文详细介绍了使用JavaScript实现点击目录变换颜色的完整方案,包括:

  1. 基础HTML/CSS结构搭建
  2. JavaScript事件处理核心逻辑
  3. 状态持久化存储方案
  4. 可访问性优化(键盘导航)
  5. 性能优化技巧
  6. 浏览器兼容性处理
  7. 组件化封装思路

通过这种实现方式,不仅可以提升用户体验,还能为后续功能扩展打下良好基础。开发者可以根据实际需求,进一步添加滚动定位、动态加载等高级功能。 “`

推荐阅读:
  1. 自定义Button被点击的颜色变化效果
  2. JS如何实现点击按钮随机生成可拖动的不同颜色块示例

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

js

上一篇:java中的装饰者模式是什么

下一篇:java中的桥接模式是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》