DOM基础概念

什么是DOM

DOM(Document Object Model,文档对象模型)是HTML和XML文档的编程接口。它将文档表示为节点树,每个节点都是一个对象,代表文档的一部分。

<!DOCTYPE html>
<html>
<head>
    <title>DOM示例</title>
</head>
<body>
    <div id="container">
        <h1 class="title">标题</h1>
        <p class="content">这是一段文本</p>
        <ul>
            <li>项目1</li>
            <li>项目2</li>
        </ul>
    </div>
</body>
</html>
// DOM树结构
// document
//   └── html
//       ├── head
//       │   └── title
//       │       └── "DOM示例" (文本节点)
//       └── body
//           └── div#container
//               ├── h1.title
//               │   └── "标题" (文本节点)
//               ├── p.content
//               │   └── "这是一段文本" (文本节点)
//               └── ul
//                   ├── li
//                   │   └── "项目1" (文本节点)
//                   └── li
//                       └── "项目2" (文本节点)

// DOM节点类型
console.log('文档节点:', document.nodeType); // 9
console.log('元素节点:', document.body.nodeType); // 1
console.log('文本节点:', document.body.firstChild.nodeType); // 3
console.log('注释节点:', 8); // 注释节点类型

DOM节点关系

// 获取DOM元素
const container = document.getElementById('container');
const title = document.querySelector('.title');
const items = document.querySelectorAll('li');

// 父子关系
console.log('父节点:', title.parentNode);
console.log('父元素:', title.parentElement);
console.log('子节点:', container.childNodes);
console.log('子元素:', container.children);
console.log('第一个子节点:', container.firstChild);
console.log('第一个子元素:', container.firstElementChild);
console.log('最后一个子节点:', container.lastChild);
console.log('最后一个子元素:', container.lastElementChild);

// 兄弟关系
console.log('下一个兄弟节点:', title.nextSibling);
console.log('下一个兄弟元素:', title.nextElementSibling);
console.log('上一个兄弟节点:', title.previousSibling);
console.log('上一个兄弟元素:', title.previousElementSibling);

// 遍历DOM树
function traverseDOM(node, depth = 0) {
  const indent = '  '.repeat(depth);
  
  if (node.nodeType === Node.ELEMENT_NODE) {
    console.log(`${indent}元素: ${node.tagName.toLowerCase()}`);
    
    // 遍历属性
    if (node.attributes.length > 0) {
      for (const attr of node.attributes) {
        console.log(`${indent}  属性: ${attr.name}="${attr.value}"`);
      }
    }
    
    // 遍历子节点
    for (const child of node.childNodes) {
      traverseDOM(child, depth + 1);
    }
  } else if (node.nodeType === Node.TEXT_NODE) {
    const text = node.textContent.trim();
    if (text) {
      console.log(`${indent}文本: "${text}"`);
    }
  }
}

// traverseDOM(document.body);

元素选择和查找

基本选择方法

// 1. 通过ID选择(返回单个元素或null)
const elementById = document.getElementById('container');
console.log('通过ID选择:', elementById);

// 2. 通过类名选择(返回HTMLCollection)
const elementsByClass = document.getElementsByClassName('content');
console.log('通过类名选择:', elementsByClass);

// 3. 通过标签名选择(返回HTMLCollection)
const elementsByTag = document.getElementsByTagName('li');
console.log('通过标签名选择:', elementsByTag);

// 4. 通过name属性选择(返回NodeList)
const elementsByName = document.getElementsByName('username');
console.log('通过name选择:', elementsByName);

// 5. CSS选择器(返回第一个匹配元素或null)
const elementBySelector = document.querySelector('.title');
console.log('CSS选择器(单个):', elementBySelector);

// 6. CSS选择器(返回所有匹配元素的NodeList)
const elementsBySelector = document.querySelectorAll('li');
console.log('CSS选择器(多个):', elementsBySelector);

// HTMLCollection vs NodeList
console.log('HTMLCollection是否为数组:', Array.isArray(elementsByClass)); // false
console.log('NodeList是否为数组:', Array.isArray(elementsBySelector)); // false

// 转换为数组
const classArray = Array.from(elementsByClass);
const selectorArray = [...elementsBySelector];
console.log('转换为数组:', classArray, selectorArray);

高级选择技巧

// 复杂CSS选择器
const examples = {
  // 后代选择器
  descendants: document.querySelectorAll('#container p'),
  
  // 直接子元素选择器
  directChildren: document.querySelectorAll('#container > li'),
  
  // 相邻兄弟选择器
  adjacentSibling: document.querySelectorAll('h1 + p'),
  
  // 通用兄弟选择器
  generalSibling: document.querySelectorAll('h1 ~ p'),
  
  // 属性选择器
  byAttribute: document.querySelectorAll('[data-id]'),
  byAttributeValue: document.querySelectorAll('[data-id="123"]'),
  byAttributeContains: document.querySelectorAll('[class*="btn"]'),
  byAttributeStarts: document.querySelectorAll('[class^="nav"]'),
  byAttributeEnds: document.querySelectorAll('[class$="active"]'),
  
  // 伪类选择器
  firstChild: document.querySelectorAll('li:first-child'),
  lastChild: document.querySelectorAll('li:last-child'),
  nthChild: document.querySelectorAll('li:nth-child(2n)'), // 偶数项
  notSelector: document.querySelectorAll('li:not(.disabled)'),
  
  // 伪元素选择器(注意:querySelector不能选择伪元素)
  // before: document.querySelectorAll('p::before'), // 无效
  
  // 组合选择器
  multiple: document.querySelectorAll('h1, h2, h3'),
  complex: document.querySelectorAll('#container .content:not(.hidden)')
};

console.log('高级选择器示例:', examples);

// 自定义选择器函数
function selectElements(selector, context = document) {
  try {
    const elements = context.querySelectorAll(selector);
    return Array.from(elements);
  } catch (error) {
    console.error('无效的选择器:', selector, error);
    return [];
  }
}

// 链式选择器
class ElementSelector {
  constructor(elements) {
    this.elements = Array.isArray(elements) ? elements : [elements];
  }
  
  static select(selector) {
    const elements = Array.from(document.querySelectorAll(selector));
    return new ElementSelector(elements);
  }
  
  filter(selector) {
    const filtered = this.elements.filter(el => el.matches(selector));
    return new ElementSelector(filtered);
  }
  
  find(selector) {
    const found = [];
    this.elements.forEach(el => {
      found.push(...el.querySelectorAll(selector));
    });
    return new ElementSelector(found);
  }
  
  parent() {
    const parents = this.elements.map(el => el.parentElement).filter(Boolean);
    return new ElementSelector(parents);
  }
  
  children(selector) {
    const children = [];
    this.elements.forEach(el => {
      const childElements = Array.from(el.children);
      if (selector) {
        children.push(...childElements.filter(child => child.matches(selector)));
      } else {
        children.push(...childElements);
      }
    });
    return new ElementSelector(children);
  }
  
  get(index) {
    return this.elements[index] || null;
  }
  
  first() {
    return this.elements[0] || null;
  }
  
  last() {
    return this.elements[this.elements.length - 1] || null;
  }
  
  length() {
    return this.elements.length;
  }
  
  toArray() {
    return [...this.elements];
  }
}

// 使用链式选择器
const selectedElements = ElementSelector
  .select('#container')
  .find('li')
  .filter(':not(.disabled)');

console.log('链式选择结果:', selectedElements.toArray());

元素内容操作

文本内容操作

// 创建测试元素
const testDiv = document.createElement('div');
testDiv.innerHTML = `
  <h2>标题</h2>
  <p>段落文本 <strong>粗体</strong> 更多文本</p>
  <!-- 这是注释 -->
`;
document.body.appendChild(testDiv);

// textContent vs innerText vs innerHTML
console.log('textContent:', testDiv.textContent);
// 输出:"\n  标题\n  段落文本 粗体 更多文本\n  \n"

console.log('innerText:', testDiv.innerText);
// 输出:"标题\n段落文本 粗体 更多文本"

console.log('innerHTML:', testDiv.innerHTML);
// 输出:"\n  <h2>标题</h2>\n  <p>段落文本 <strong>粗体</strong> 更多文本</p>\n  <!-- 这是注释 -->\n"

// 设置内容
const contentDiv = document.createElement('div');

// 设置纯文本(安全,会转义HTML)
contentDiv.textContent = '<script>alert("XSS")</script>普通文本';
console.log('textContent设置后:', contentDiv.innerHTML);
// 输出:"&lt;script&gt;alert("XSS")&lt;/script&gt;普通文本"

// 设置HTML(不安全,会执行HTML)
contentDiv.innerHTML = '<em>强调文本</em> 和 <strong>重要文本</strong>';
console.log('innerHTML设置后:', contentDiv.innerHTML);

// 安全的HTML设置
function setHTMLSafely(element, htmlString) {
  // 创建临时元素
  const temp = document.createElement('div');
  temp.innerHTML = htmlString;
  
  // 移除所有脚本标签
  const scripts = temp.querySelectorAll('script');
  scripts.forEach(script => script.remove());
  
  // 移除危险属性
  const allElements = temp.querySelectorAll('*');
  allElements.forEach(el => {
    const dangerousAttrs = ['onclick', 'onload', 'onerror', 'onmouseover'];
    dangerousAttrs.forEach(attr => {
      if (el.hasAttribute(attr)) {
        el.removeAttribute(attr);
      }
    });
  });
  
  element.innerHTML = temp.innerHTML;
}

// 文本操作工具函数
class TextUtils {
  static getTextLength(element) {
    return element.textContent.length;
  }
  
  static getWordCount(element) {
    const text = element.textContent.trim();
    return text ? text.split(/\s+/).length : 0;
  }
  
  static truncateText(element, maxLength, suffix = '...') {
    const text = element.textContent;
    if (text.length <= maxLength) return;
    
    const truncated = text.slice(0, maxLength - suffix.length);
    element.textContent = truncated + suffix;
  }
  
  static highlightText(element, searchTerm, className = 'highlight') {
    const text = element.textContent;
    const regex = new RegExp(`(${searchTerm})`, 'gi');
    const highlightedHTML = text.replace(regex, `<span class="${className}">$1</span>`);
    element.innerHTML = highlightedHTML;
  }
  
  static removeHighlight(element) {
    const highlighted = element.querySelectorAll('.highlight');
    highlighted.forEach(span => {
      span.outerHTML = span.textContent;
    });
  }
}

// 使用文本工具
const textElement = document.createElement('p');
textElement.textContent = 'JavaScript是一种强大的编程语言,广泛用于Web开发。';
document.body.appendChild(textElement);

console.log('文本长度:', TextUtils.getTextLength(textElement));
console.log('单词数量:', TextUtils.getWordCount(textElement));

// TextUtils.truncateText(textElement, 20);
// TextUtils.highlightText(textElement, 'JavaScript');

HTML内容操作

// innerHTML的高级用法
class HTMLBuilder {
  constructor() {
    this.html = '';
  }
  
  tag(tagName, content = '', attributes = {}) {
    const attrs = Object.entries(attributes)
      .map(([key, value]) => `${key}="${value}"`)
      .join(' ');
    
    const attrString = attrs ? ` ${attrs}` : '';
    this.html += `<${tagName}${attrString}>${content}</${tagName}>`;
    return this;
  }
  
  text(content) {
    this.html += content;
    return this;
  }
  
  br() {
    this.html += '<br>';
    return this;
  }
  
  build() {
    return this.html;
  }
  
  clear() {
    this.html = '';
    return this;
  }
}

// 使用HTML构建器
const htmlBuilder = new HTMLBuilder();
const generatedHTML = htmlBuilder
  .tag('div', '', { class: 'container', id: 'main' })
  .tag('h1', '欢迎', { class: 'title' })
  .tag('p', '这是一个段落')
  .br()
  .tag('button', '点击我', { class: 'btn btn-primary', 'data-action': 'click' })
  .build();

console.log('生成的HTML:', generatedHTML);

// 模板系统
class SimpleTemplate {
  constructor(template) {
    this.template = template;
  }
  
  render(data) {
    let result = this.template;
    
    // 替换变量 {{variable}}
    result = result.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => {
      return data[key] !== undefined ? data[key] : match;
    });
    
    // 处理条件 {{#if condition}}...{{/if}}
    result = result.replace(/\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, condition, content) => {
      return data[condition] ? content : '';
    });
    
    // 处理循环 {{#each array}}...{{/each}}
    result = result.replace(/\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g, (match, arrayName, itemTemplate) => {
      const array = data[arrayName];
      if (!Array.isArray(array)) return '';
      
      return array.map(item => {
        let itemHTML = itemTemplate;
        // 替换 {{this}} 为当前项
        itemHTML = itemHTML.replace(/\{\{this\}\}/g, item);
        // 如果item是对象,替换其属性
        if (typeof item === 'object') {
          Object.keys(item).forEach(key => {
            const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
            itemHTML = itemHTML.replace(regex, item[key]);
          });
        }
        return itemHTML;
      }).join('');
    });
    
    return result;
  }
}

// 使用模板系统
const template = new SimpleTemplate(`
  <div class="user-card">
    <h2>{{name}}</h2>
    {{#if email}}
      <p>邮箱: {{email}}</p>
    {{/if}}
    <h3>技能:</h3>
    <ul>
      {{#each skills}}
        <li>{{this}}</li>
      {{/each}}
    </ul>
    <h3>项目:</h3>
    <ul>
      {{#each projects}}
        <li>
          <strong>{{name}}</strong> - {{description}}
        </li>
      {{/each}}
    </ul>
  </div>
`);

const userData = {
  name: '张三',
  email: 'zhangsan@example.com',
  skills: ['JavaScript', 'React', 'Node.js'],
  projects: [
    { name: '项目A', description: '一个很棒的项目' },
    { name: '项目B', description: '另一个项目' }
  ]
};

const renderedHTML = template.render(userData);
console.log('渲染的HTML:', renderedHTML);

// 创建并插入到页面
const templateContainer = document.createElement('div');
templateContainer.innerHTML = renderedHTML;
document.body.appendChild(templateContainer);

元素属性操作

标准属性操作

// 创建测试元素
const testElement = document.createElement('input');
testElement.type = 'text';
testElement.id = 'test-input';
testElement.className = 'form-control';
document.body.appendChild(testElement);

// 获取属性
console.log('ID属性:', testElement.id);
console.log('类名:', testElement.className);
console.log('类型:', testElement.type);

// 使用getAttribute/setAttribute
console.log('getAttribute ID:', testElement.getAttribute('id'));
console.log('getAttribute class:', testElement.getAttribute('class'));

// 设置属性
testElement.setAttribute('placeholder', '请输入文本');
testElement.setAttribute('data-validation', 'required');
testElement.setAttribute('maxlength', '100');

// 检查属性是否存在
console.log('是否有placeholder:', testElement.hasAttribute('placeholder'));
console.log('是否有title:', testElement.hasAttribute('title'));

// 移除属性
testElement.removeAttribute('maxlength');

// 获取所有属性
console.log('所有属性:');
for (const attr of testElement.attributes) {
  console.log(`${attr.name}: ${attr.value}`);
}

// 属性操作工具类
class AttributeManager {
  constructor(element) {
    this.element = element;
  }
  
  set(name, value) {
    this.element.setAttribute(name, value);
    return this;
  }
  
  get(name) {
    return this.element.getAttribute(name);
  }
  
  has(name) {
    return this.element.hasAttribute(name);
  }
  
  remove(name) {
    this.element.removeAttribute(name);
    return this;
  }
  
  toggle(name, value) {
    if (this.has(name)) {
      this.remove(name);
    } else {
      this.set(name, value || '');
    }
    return this;
  }
  
  setMultiple(attributes) {
    Object.entries(attributes).forEach(([name, value]) => {
      this.set(name, value);
    });
    return this;
  }
  
  getAll() {
    const attrs = {};
    for (const attr of this.element.attributes) {
      attrs[attr.name] = attr.value;
    }
    return attrs;
  }
  
  copy(targetElement) {
    for (const attr of this.element.attributes) {
      targetElement.setAttribute(attr.name, attr.value);
    }
    return this;
  }
}

// 使用属性管理器
const attrManager = new AttributeManager(testElement);
attrManager
  .set('data-role', 'input')
  .set('aria-label', '文本输入框')
  .setMultiple({
    'data-validate': 'true',
    'data-min-length': '3'
  });

console.log('所有属性:', attrManager.getAll());

数据属性操作

// 数据属性(data-*)操作
const dataElement = document.createElement('div');
document.body.appendChild(dataElement);

// 使用dataset API
dataElement.dataset.userId = '123';
dataElement.dataset.userName = 'john_doe';
dataElement.dataset.isActive = 'true';
dataElement.dataset.createdAt = new Date().toISOString();

// 复杂数据类型
dataElement.dataset.config = JSON.stringify({
  theme: 'dark',
  language: 'zh-CN',
  features: ['feature1', 'feature2']
});

// 读取数据属性
console.log('用户ID:', dataElement.dataset.userId);
console.log('用户名:', dataElement.dataset.userName);
console.log('是否激活:', dataElement.dataset.isActive === 'true');

// 解析复杂数据
const config = JSON.parse(dataElement.dataset.config);
console.log('配置:', config);

// 遍历所有数据属性
console.log('所有数据属性:');
for (const [key, value] of Object.entries(dataElement.dataset)) {
  console.log(`${key}: ${value}`);
}

// 数据属性管理器
class DataManager {
  constructor(element) {
    this.element = element;
  }
  
  set(key, value) {
    if (typeof value === 'object') {
      this.element.dataset[key] = JSON.stringify(value);
    } else {
      this.element.dataset[key] = String(value);
    }
    return this;
  }
  
  get(key, defaultValue = null) {
    const value = this.element.dataset[key];
    if (value === undefined) return defaultValue;
    
    // 尝试解析JSON
    try {
      return JSON.parse(value);
    } catch {
      return value;
    }
  }
  
  getNumber(key, defaultValue = 0) {
    const value = this.get(key);
    const num = Number(value);
    return isNaN(num) ? defaultValue : num;
  }
  
  getBoolean(key, defaultValue = false) {
    const value = this.get(key);
    if (typeof value === 'boolean') return value;
    if (typeof value === 'string') {
      return value.toLowerCase() === 'true';
    }
    return defaultValue;
  }
  
  getArray(key, defaultValue = []) {
    const value = this.get(key);
    return Array.isArray(value) ? value : defaultValue;
  }
  
  has(key) {
    return key in this.element.dataset;
  }
  
  remove(key) {
    delete this.element.dataset[key];
    return this;
  }
  
  clear() {
    for (const key in this.element.dataset) {
      delete this.element.dataset[key];
    }
    return this;
  }
  
  getAll() {
    const data = {};
    for (const [key, value] of Object.entries(this.element.dataset)) {
      data[key] = this.get(key);
    }
    return data;
  }
}

// 使用数据管理器
const dataManager = new DataManager(dataElement);
dataManager
  .set('score', 95)
  .set('tags', ['javascript', 'dom', 'web'])
  .set('metadata', { version: '1.0', author: 'developer' });

console.log('分数:', dataManager.getNumber('score'));
console.log('标签:', dataManager.getArray('tags'));
console.log('元数据:', dataManager.get('metadata'));
console.log('所有数据:', dataManager.getAll());

类名操作

// 创建测试元素
const classElement = document.createElement('div');
classElement.className = 'container active';
document.body.appendChild(classElement);

// 传统方式操作类名
console.log('当前类名:', classElement.className);
classElement.className += ' highlight';
classElement.className = classElement.className.replace('active', 'inactive');

// 使用classList API(推荐)
const classList = classElement.classList;

// 添加类名
classList.add('new-class');
classList.add('class1', 'class2', 'class3'); // 添加多个

// 移除类名
classList.remove('highlight');
classList.remove('class1', 'class2'); // 移除多个

// 切换类名
classList.toggle('active'); // 如果存在则移除,不存在则添加
classList.toggle('visible', true); // 强制添加
classList.toggle('hidden', false); // 强制移除

// 检查类名
console.log('是否包含container:', classList.contains('container'));
console.log('是否包含active:', classList.contains('active'));

// 替换类名
classList.replace('inactive', 'active');

// 遍历类名
console.log('所有类名:');
classList.forEach((className, index) => {
  console.log(`${index}: ${className}`);
});

// 类名管理器
class ClassManager {
  constructor(element) {
    this.element = element;
    this.classList = element.classList;
  }
  
  add(...classNames) {
    this.classList.add(...classNames);
    return this;
  }
  
  remove(...classNames) {
    this.classList.remove(...classNames);
    return this;
  }
  
  toggle(className, force) {
    this.classList.toggle(className, force);
    return this;
  }
  
  replace(oldClass, newClass) {
    this.classList.replace(oldClass, newClass);
    return this;
  }
  
  contains(className) {
    return this.classList.contains(className);
  }
  
  clear() {
    this.element.className = '';
    return this;
  }
  
  set(classNames) {
    if (Array.isArray(classNames)) {
      this.element.className = classNames.join(' ');
    } else {
      this.element.className = classNames;
    }
    return this;
  }
  
  get() {
    return Array.from(this.classList);
  }
  
  hasAny(...classNames) {
    return classNames.some(className => this.contains(className));
  }
  
  hasAll(...classNames) {
    return classNames.every(className => this.contains(className));
  }
  
  addIf(condition, ...classNames) {
    if (condition) {
      this.add(...classNames);
    }
    return this;
  }
  
  removeIf(condition, ...classNames) {
    if (condition) {
      this.remove(...classNames);
    }
    return this;
  }
  
  toggleGroup(groupClasses, activeClass) {
    // 移除组中的所有类,然后添加激活的类
    this.remove(...groupClasses);
    this.add(activeClass);
    return this;
  }
}

// 使用类名管理器
const classManager = new ClassManager(classElement);
classManager
  .add('btn', 'btn-primary')
  .addIf(true, 'enabled')
  .toggleGroup(['small', 'medium', 'large'], 'medium');

console.log('最终类名:', classManager.get());

// 条件类名应用
function applyConditionalClasses(element, conditions) {
  const manager = new ClassManager(element);
  
  Object.entries(conditions).forEach(([className, condition]) => {
    if (typeof condition === 'function') {
      manager.addIf(condition(), className);
    } else {
      manager.addIf(condition, className);
    }
  });
  
  return manager;
}

// 使用条件类名
const testDiv = document.createElement('div');
const isLoggedIn = true;
const userRole = 'admin';
const score = 85;

applyConditionalClasses(testDiv, {
  'logged-in': isLoggedIn,
  'guest': !isLoggedIn,
  'admin': userRole === 'admin',
  'high-score': score > 80,
  'perfect-score': () => score === 100
});

console.log('条件类名结果:', testDiv.className);

样式操作

内联样式操作

// 创建测试元素
const styleElement = document.createElement('div');
styleElement.textContent = '样式测试元素';
document.body.appendChild(styleElement);

// 直接设置样式属性
styleElement.style.color = 'red';
styleElement.style.fontSize = '20px';
styleElement.style.backgroundColor = '#f0f0f0';
styleElement.style.padding = '10px';
styleElement.style.border = '2px solid blue';

// 使用cssText一次性设置多个样式
styleElement.style.cssText = `
  color: green;
  font-size: 24px;
  background-color: yellow;
  padding: 15px;
  border-radius: 5px;
  margin: 10px 0;
`;

// 获取样式值
console.log('颜色:', styleElement.style.color);
console.log('字体大小:', styleElement.style.fontSize);
console.log('所有内联样式:', styleElement.style.cssText);

// 移除样式属性
styleElement.style.removeProperty('background-color');
// 或者设置为空字符串
styleElement.style.backgroundColor = '';

// 样式操作工具类
class StyleManager {
  constructor(element) {
    this.element = element;
    this.style = element.style;
  }
  
  set(property, value) {
    if (typeof property === 'object') {
      // 批量设置
      Object.entries(property).forEach(([prop, val]) => {
        this.style[this.camelCase(prop)] = val;
      });
    } else {
      this.style[this.camelCase(property)] = value;
    }
    return this;
  }
  
  get(property) {
    return this.style[this.camelCase(property)];
  }
  
  remove(property) {
    this.style.removeProperty(this.kebabCase(property));
    return this;
  }
  
  toggle(property, value1, value2) {
    const current = this.get(property);
    this.set(property, current === value1 ? value2 : value1);
    return this;
  }
  
  has(property) {
    return this.get(property) !== '';
  }
  
  clear() {
    this.style.cssText = '';
    return this;
  }
  
  copy(targetElement) {
    targetElement.style.cssText = this.style.cssText;
    return this;
  }
  
  // 工具方法:转换为驼峰命名
  camelCase(str) {
    return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase());
  }
  
  // 工具方法:转换为短横线命名
  kebabCase(str) {
    return str.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
  }
  
  // 动画相关方法
  animate(keyframes, options = {}) {
    return this.element.animate(keyframes, {
      duration: 300,
      easing: 'ease',
      ...options
    });
  }
  
  fadeIn(duration = 300) {
    this.set('opacity', '0');
    return this.animate([
      { opacity: 0 },
      { opacity: 1 }
    ], { duration });
  }
  
  fadeOut(duration = 300) {
    return this.animate([
      { opacity: 1 },
      { opacity: 0 }
    ], { duration });
  }
  
  slideDown(duration = 300) {
    const height = this.element.scrollHeight;
    this.set({ height: '0', overflow: 'hidden' });
    
    return this.animate([
      { height: '0px' },
      { height: `${height}px` }
    ], { duration });
  }
  
  slideUp(duration = 300) {
    const height = this.element.scrollHeight;
    
    return this.animate([
      { height: `${height}px` },
      { height: '0px' }
    ], { duration });
  }
}

// 使用样式管理器
const styleManager = new StyleManager(styleElement);
styleManager
  .set({
    'background-color': 'lightblue',
    'border-radius': '10px',
    'box-shadow': '0 2px 4px rgba(0,0,0,0.1)',
    'transition': 'all 0.3s ease'
  })
  .set('transform', 'scale(1.1)');

// 计算样式获取
function getComputedStyles(element, properties) {
  const computed = window.getComputedStyle(element);
  
  if (Array.isArray(properties)) {
    const result = {};
    properties.forEach(prop => {
      result[prop] = computed.getPropertyValue(prop);
    });
    return result;
  } else if (properties) {
    return computed.getPropertyValue(properties);
  } else {
    // 返回所有计算样式
    const allStyles = {};
    for (let i = 0; i < computed.length; i++) {
      const prop = computed[i];
      allStyles[prop] = computed.getPropertyValue(prop);
    }
    return allStyles;
  }
}

// 获取计算样式示例
console.log('计算后的颜色:', getComputedStyles(styleElement, 'color'));
console.log('多个样式:', getComputedStyles(styleElement, ['width', 'height', 'margin']));

CSS类和样式表操作

// 动态创建样式表
function createStyleSheet(css) {
  const style = document.createElement('style');
  style.textContent = css;
  document.head.appendChild(style);
  return style;
}

// 创建主题样式
const themeStyles = createStyleSheet(`
  .theme-dark {
    background-color: #333;
    color: #fff;
  }
  
  .theme-light {
    background-color: #fff;
    color: #333;
  }
  
  .btn-animated {
    transition: all 0.3s ease;
    transform: scale(1);
  }
  
  .btn-animated:hover {
    transform: scale(1.05);
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  }
  
  .fade-in {
    animation: fadeIn 0.5s ease-in;
  }
  
  @keyframes fadeIn {
    from { opacity: 0; transform: translateY(20px); }
    to { opacity: 1; transform: translateY(0); }
  }
`);

// 样式表管理器
class StyleSheetManager {
  constructor() {
    this.sheets = new Map();
  }
  
  create(id, css) {
    if (this.sheets.has(id)) {
      this.update(id, css);
      return this.sheets.get(id);
    }
    
    const style = document.createElement('style');
    style.id = id;
    style.textContent = css;
    document.head.appendChild(style);
    
    this.sheets.set(id, style);
    return style;
  }
  
  update(id, css) {
    const style = this.sheets.get(id);
    if (style) {
      style.textContent = css;
    }
    return style;
  }
  
  remove(id) {
    const style = this.sheets.get(id);
    if (style) {
      style.remove();
      this.sheets.delete(id);
    }
    return this;
  }
  
  toggle(id, css) {
    if (this.sheets.has(id)) {
      this.remove(id);
    } else {
      this.create(id, css);
    }
    return this;
  }
  
  exists(id) {
    return this.sheets.has(id);
  }
  
  getAll() {
    return Array.from(this.sheets.keys());
  }
}

// 使用样式表管理器
const styleManager = new StyleSheetManager();

// 创建响应式样式
styleManager.create('responsive', `
  @media (max-width: 768px) {
    .responsive-text {
      font-size: 14px;
    }
  }
  
  @media (min-width: 769px) {
    .responsive-text {
      font-size: 18px;
    }
  }
`);

// 主题切换器
class ThemeManager {
  constructor() {
    this.currentTheme = 'light';
    this.themes = {
      light: {
        '--bg-color': '#ffffff',
        '--text-color': '#333333',
        '--border-color': '#dddddd',
        '--accent-color': '#007bff'
      },
      dark: {
        '--bg-color': '#333333',
        '--text-color': '#ffffff',
        '--border-color': '#555555',
        '--accent-color': '#66b3ff'
      },
      blue: {
        '--bg-color': '#e3f2fd',
        '--text-color': '#0d47a1',
        '--border-color': '#90caf9',
        '--accent-color': '#1976d2'
      }
    };
    
    this.init();
  }
  
  init() {
    // 创建CSS变量样式
    const css = `
      :root {
        ${this.getCSSVariables(this.currentTheme)}
      }
      
      body {
        background-color: var(--bg-color);
        color: var(--text-color);
        transition: all 0.3s ease;
      }
      
      .themed-element {
        border: 1px solid var(--border-color);
        background-color: var(--bg-color);
        color: var(--text-color);
      }
      
      .accent {
        color: var(--accent-color);
      }
    `;
    
    const styleManager = new StyleSheetManager();
    styleManager.create('theme-variables', css);
  }
  
  getCSSVariables(themeName) {
    const theme = this.themes[themeName];
    return Object.entries(theme)
      .map(([key, value]) => `${key}: ${value};`)
      .join('\n        ');
  }
  
  setTheme(themeName) {
    if (!this.themes[themeName]) {
      console.warn(`主题 '${themeName}' 不存在`);
      return;
    }
    
    this.currentTheme = themeName;
    
    // 更新CSS变量
    const root = document.documentElement;
    Object.entries(this.themes[themeName]).forEach(([key, value]) => {
      root.style.setProperty(key, value);
    });
    
    // 触发主题变更事件
    document.dispatchEvent(new CustomEvent('themechange', {
      detail: { theme: themeName }
    }));
  }
  
  getTheme() {
    return this.currentTheme;
  }
  
  getAvailableThemes() {
    return Object.keys(this.themes);
  }
  
  addTheme(name, variables) {
    this.themes[name] = variables;
  }
}

// 使用主题管理器
const themeManager = new ThemeManager();

// 创建主题切换按钮
const themeButton = document.createElement('button');
themeButton.textContent = '切换主题';
themeButton.className = 'themed-element';
themeButton.style.cssText = 'padding: 10px; margin: 10px; cursor: pointer;';

let themeIndex = 0;
const themes = themeManager.getAvailableThemes();

themeButton.addEventListener('click', () => {
  themeIndex = (themeIndex + 1) % themes.length;
  themeManager.setTheme(themes[themeIndex]);
  themeButton.textContent = `当前主题: ${themes[themeIndex]}`;
});

document.body.appendChild(themeButton);

// 监听主题变更
document.addEventListener('themechange', (event) => {
  console.log('主题已切换到:', event.detail.theme);
});

本章总结

本章详细介绍了DOM操作的核心概念和技术:

  1. DOM基础:理解了DOM树结构、节点类型和节点关系
  2. 元素选择:掌握了各种元素选择方法,包括传统方法和现代CSS选择器
  3. 内容操作:学习了文本和HTML内容的读取、设置和安全处理
  4. 属性操作:了解了标准属性、数据属性和类名的操作方法
  5. 样式操作:掌握了内联样式、CSS类和动态样式表的管理
  6. 工具类设计:通过各种管理器类展示了如何封装DOM操作

DOM操作是前端开发的基础技能,熟练掌握这些技术能够帮助我们构建动态、交互性强的Web应用。这些知识为后续学习事件处理、动画效果等高级主题奠定了坚实基础。

下一章我们将学习事件处理机制,包括事件监听、事件委托和自定义事件。