普通视图

发现新文章,点击刷新页面。
昨天 — 2025年5月17日首页

常用DOM

作者 咔咔库奇
2025年5月17日 16:39

目录

获取DOM节点

通过ID查找节点

通过标签名查找节点

通过类名查找节点

通过CSS选择器查找单个节点

通过CSS选择器查找所有节点

通过关系获取节点

获取下一个兄弟节点

获取上一个兄弟节点

获取所有子节点(元素节点)

获取父级节点

创建、删除、替换和克隆节点

创建节点

删除节点(自删除)

删除子节点

替换节点

克隆节点

操作节点属性

设置属性

获取属性

删除属性

类名操作

直接设置类名(会覆盖原有的类名)

使用classList操作类名

向标签内部写入内容

写入文本

事件处理

基本事件

阻止事件冒泡

阻止默认行为

输入框事件

键盘事件

绑定事件(不推荐,因为无法绑定多个事件处理器)

事件监听(推荐方式)

移除事件监听


获取DOM节点

  1. 通过ID查找节点

    let element = document.getElementById('elementId');
    

  2. 通过标签名查找节点

    let elements = document.getElementsByTagName('tagName'); // 返回HTMLCollection
    

  3. 通过类名查找节点

    let elements = document.getElementsByClassName('className'); 
    // 返回HTMLCollection
    

  4. 通过CSS选择器查找单个节点

    let element = document.querySelector('.className #elementId'); 
    // 返回第一个匹配的元素
    

  5. 通过CSS选择器查找所有节点

    let elements = document.querySelectorAll('.className, #elementId'); 
    // 返回NodeList
    

通过关系获取节点

  1. 获取下一个兄弟节点

    let nextSibling = element.nextElementSibling;
    

  2. 获取上一个兄弟节点

    let prevSibling = element.previousElementSibling;
    

  3. 获取所有子节点(元素节点)

    let children = element.children; // 返回HTMLCollection,只包含元素节点
    

  4. 获取父级节点

    let parent = element.parentNode; 
    //如果需要父元素节点,可以使用element.parentElement
    

创建、删除、替换和克隆节点

  1. 创建节点

    let newElement = document.createElement('tagName');
    

  2. 删除节点(自删除)

    element.remove();
    

  3. 删除子节点

    parentElement.removeChild(childNode);
    

  4. 替换节点

    parentElement.replaceChild(newNode, oldNode);
    

  5. 克隆节点

    let clonedNode = originalNode.cloneNode(true); 
    // true表示深拷贝,false表示浅拷贝
    

操作节点属性

  1. 设置属性

    element.setAttribute('attributeName', 'value');
    

  2. 获取属性

    let value = element.getAttribute('attributeName');
    

  3. 删除属性

    element.removeAttribute('attributeName');
    

类名操作

  1. 直接设置类名(会覆盖原有的类名)

    element.className = 'newClassName';
    

  2. 使用classList操作类名

    element.classList.add('className'); // 添加
    element.classList.remove('className'); // 删除
    element.classList.toggle('className'); // 切换
    let hasClass = element.classList.contains('className'); // 判断是否存在
    

向标签内部写入内容

  1. 写入文本

    element.innerText = 'text'; // 不会解析HTML
    element.textContent = 'text'; // 与innerText类似,但兼容性更好
    

  2. 写入文本或标签

    element.innerHTML = '<p>HTML content</p>'; // 会解析HTML
    

事件处理

  1. 基本事件

    element.onclick = function() { /* code */ };
    element.onmouseenter = function() { /* code */ };
    element.onmouseleave = function() { /* code */ };
    element.onmousemove = function() { /* code */ };
    

  2. 阻止事件冒泡

    event.stopPropagation();
    

  3. 阻止默认行为

    event.preventDefault();
    

  4. 输入框事件

    inputElement.onblur = function() { /* code when element loses focus */ };
    inputElement.onfocus = function() { /* code when element gains focus */ };
    inputElement.oninput = function() { /* code when input value changes */ };
    inputElement.onchange = function() { 
    /* code when input value is committed (e.g., after losing focus and value has changed) */ 
    };
    

  5. 键盘事件

    element.onkeydown = function(event) { 
    /* code; event.keyCode is deprecated, use event.key or event.code */ 
    };
    element.onkeyup = function(event) { /* code */ };
    

  6. 绑定事件(不推荐,因为无法绑定多个事件处理器)

    element.onclick = function() { /* code */ };
    

  7. 事件监听(推荐方式)

    element.addEventListener('click', function(event) { /* code */ });
    element.addEventListener('mouseenter', function(event) { /* code */ }, false); 
    // 第三个参数为捕获阶段(可选,默认为false,即冒泡阶段)
    

  8. 移除事件监听

    element.removeEventListener('click', functionReference); 
    // 必须传递与添加时相同的函数引用
    

注意:event.keyCode 在现代浏览器中已被弃用,建议使用 event.key 或 event.code 来获取键盘事件的键值。同时,当使用 addEventListener 和 removeEventListener 时,传递给 removeEventListener 的函数必须与传递给 addEventListener 的函数完全相同(通常是同一个函数引用)。如果传递的是匿名函数,则无法正确移除事件监听器。

大学生常用-原生js实现:点击切换图片,轮播图,tab切换,分页符,秒杀等等......直接copy就能使

作者 咔咔库奇
2025年5月17日 16:38

目录

一、交互组件类

轮播图实现

Tab切换功能

模态框 (Modal)

下拉菜单

手风琴效果

自定义右键菜单

二、表单处理类

表单验证增强

密码强度检测(输入安全相关)

三、数据存储类

本地存储记忆功能(localStorage/cookie操作)

四、动态内容处理

实时搜索过滤

动态加载更多内容

随机名言生成器

五、实用工具类

网页计时器/倒计时

复制到剪贴板

简易画板功能(图形交互工具)

六、用户体验优化

回到顶部按钮

图片懒加载(性能优化)

  原生js图片放大镜效果

暗黑模式切换

网页主题色切换

七、视觉效果类

点击切换图片(基础视觉交互)

滚动动画触发

视差滚动效果


除了以下功能,还想要什么功能可写评论,作者大大看到就会回复哦

一、交互组件类

轮播图实现

 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>轮播图示例</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="carousel">
        ![404](images/image1.jpg)
        ![457](images/image2.jpg)
        ![495](images/image3.jpg)
        <!-- 可以继续添加更多图片 -->
        <div class="controls">
            <button id="prev">Prev</button>
            <button id="next">Next</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

styles.css

.carousel {
    position: relative;
    width: 600px;
    height: 400px;
    overflow: hidden;
    margin: 0 auto;
    border: 2px solid #ddd;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.carousel img {
    width: 100%;
    height: 100%;
    display: none;
    transition: opacity 0.5s ease;
}

.carousel img.active {
    display: block;
    opacity: 1;
}

.controls {
    position: absolute;
    top: 50%;
    width: 100%;
    display: flex;
    justify-content: space-between;
    transform: translateY(-50%);
}

.controls button {
    background-color: rgba(0, 0, 0, 0.5);
    color: white;
    border: none;
    padding: 10px;
    cursor: pointer;
    border-radius: 5px;
    transition: background-color 0.3s ease;
}

.controls button:hover {
    background-color: rgba(0, 0, 0, 0.7);
}

script.js

document.addEventListener('DOMContentLoaded', () => {
    let currentIndex = 0;
    const images = document.querySelectorAll('.carousel img');
    const prevButton = document.getElementById('prev');
    const nextButton = document.getElementById('next');

    function showImage(index) {
        images.forEach((img, i) => {
            img.classList.toggle('active', i === index);
        });
    }

    function showNextImage() {
        currentIndex = (currentIndex + 1) % images.length;
        showImage(currentIndex);
    }

    function showPrevImage() {
        currentIndex = (currentIndex - 1 + images.length) % images.length;
        showImage(currentIndex);
    }

    nextButton.addEventListener('click', showNextImage);
    prevButton.addEventListener('click', showPrevImage);

    // 自动播放功能(可选)
    setInterval(showNextImage, 3000); // 每3秒切换一次图片

    // 添加淡入淡出效果
    images.forEach(img => {
        img.addEventListener('transitionend', () => {
            if (!img.classList.contains('active')) {
                img.style.opacity = 0;
            }
        });
    });

    // 初始化时设置非活动图片的透明度
    images.forEach((img, i) => {
        if (i !== currentIndex) {
            img.style.opacity = 0;
        }
    });
});

Tab切换功能

 html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tab Switch</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="tabs">
        <button class="tab-button" data-tab="1">Tab 1</button>
        <button class="tab-button" data-tab="2">Tab 2</button>
        <button class="tab-button" data-tab="3">Tab 3</button>
    </div>
    <div class="tab-content">
        <div class="tab-pane" id="tab-1">Content for Tab 1</div>
        <div class="tab-pane" id="tab-2">Content for Tab 2</div>
        <div class="tab-pane" id="tab-3">Content for Tab 3</div>
    </div>
    <script src="script.js"></script>
</body>
</html>

css

/* styles.css */
body {
    font-family: Arial, sans-serif;
}

.tabs {
    display: flex;
    margin-bottom: 20px;
}

.tab-button {
    padding: 10px 20px;
    margin-right: 5px;
    background-color: #f0f0f0;
    border: 1px solid #ccc;
    cursor: pointer;
}

.tab-button:hover {
    background-color: #e0e0e0;
}

.tab-pane {
    display: none;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.tab-pane.active {
    display: block;
}

js

// script.js
document.addEventListener('DOMContentLoaded', function() {
    const tabButtons = document.querySelectorAll('.tab-button');
    const tabPanes = document.querySelectorAll('.tab-pane');

    tabButtons.forEach(button => {
        button.addEventListener('click', function() {
            tabButtons.forEach(b => b.classList.remove('active'));
            tabPanes.forEach(p => p.classList.remove('active'));

            const tabId = this.getAttribute('data-tab');
            this.classList.add('active');
            document.getElementById(`tab-${tabId}`).classList.add('active');
        });
    });

    if (tabButtons.length > 0) {
        tabButtons[0].click();
    }
});

模态框 (Modal)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>模态框示例</title>
  <style>
    .modal {
      display: none;
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.5);
      z-index: 1000;
    }
    .modal-content {
      position: relative;
      margin: 10% auto;
      padding: 20px;
      width: 80%;
      max-width: 500px;
      background-color: white;
      border-radius: 5px;
    }
    .close {
      position: absolute;
      top: 10px;
      right: 15px;
      font-size: 24px;
      cursor: pointer;
    }
    .open-modal {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <button class="open-modal">打开模态框</button>
  <div id="modal" class="modal">
    <div class="modal-content">
      <span class="close">&times;</span>
      <h2>模态框标题</h2>
      <p>这是一个模态框内容。</p>
    </div>
  </div>
  <script>
    const modal = document.getElementById('modal');
    const openBtn = document.querySelector('.open-modal');
    const closeBtn = document.querySelector('.close');

    openBtn.onclick = () => {
      modal.style.display = 'block';
    };

    closeBtn.onclick = () => {
      modal.style.display = 'none';
    };

    window.onclick = (e) => {
      if (e.target === modal) {
        modal.style.display = 'none';
      }
    };
  </script>
</body>
</html>

下拉菜单

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>下拉菜单示例</title>
  <style>
    .dropdown {
      position: relative;
      display: inline-block;
    }
    .dropdown-content {
      display: none;
      position: absolute;
      background-color: white;
      min-width: 160px;
      box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
      z-index: 1;
    }
    .dropdown-content a {
      color: black;
      padding: 12px 16px;
      text-decoration: none;
      display: block;
    }
    .dropdown-content.show {
      display: block;
    }
    .dropdown-btn {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="dropdown">
    <button class="dropdown-btn">下拉菜单</button>
    <div class="dropdown-content">
      <a href="#">链接 1</a>
      <a href="#">链接 2</a>
      <a href="#">链接 3</a>
    </div>
  </div>
  <script>
    const dropdown = document.querySelector('.dropdown');
    const dropdownContent = document.querySelector('.dropdown-content');

    dropdown.addEventListener('mouseover', () => {
      dropdownContent.classList.add('show');
    });

    dropdown.addEventListener('mouseout', () => {
      dropdownContent.classList.remove('show');
    });
  </script>
</body>
</html>

手风琴效果

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手风琴效果示例</title>
  <style>
    .accordion {
      margin: 20px 0;
    }
    .accordion-header {
      padding: 10px;
      background-color: #f1f1f1;
      cursor: pointer;
      display: flex;
      justify-content: space-between;
    }
    .accordion-content {
      padding: 0;
      max-height: 0;
      overflow: hidden;
      transition: max-height 0.3s ease-out;
    }
    .accordion-content.active {
      padding: 10px;
      max-height: 200px;
    }
    .arrow {
      transition: transform 0.3s ease;
    }
    .arrow.rotate {
      transform: rotate(180deg);
    }
  </style>
</head>
<body>
  <div class="accordion">
    <div class="accordion-header">
      <span>标题 1</span>
      <span class="arrow"></span>
    </div>
    <div class="accordion-content">
      <p>这是手风琴内容 1。</p>
    </div>
  </div>
  <div class="accordion">
    <div class="accordion-header">
      <span>标题 2</span>
      <span class="arrow"></span>
    </div>
    <div class="accordion-content">
      <p>这是手风琴内容 2。</p>
    </div>
  </div>
  <script>
    document.querySelectorAll('.accordion-header').forEach(header => {
      header.addEventListener('click', () => {
        const content = header.nextElementSibling;
        content.classList.toggle('active');
        header.querySelector('.arrow').classList.toggle('rotate');
      });
    });
  </script>
</body>
</html>

自定义右键菜单

        自定义右键菜单需要阻止默认的右键菜单,显示自定义的菜单,并根据点击位置定位。同时,点击页面其他位置时要隐藏菜单。这里可能需要动态创建菜单元素,并处理事件冒泡。

<div id="customMenu" style="display:none; position:absolute; background:#fff; box-shadow:0 0 5px #999">
  <div class="menu-item">复制</div>
  <div class="menu-item">粘贴</div>
  <div class="menu-item">刷新</div>
</div>

<script>
document.addEventListener('contextmenu', (e) => {
  e.preventDefault();
  const menu = document.getElementById('customMenu');
  menu.style.display = 'block';
  menu.style.left = e.pageX + 'px';
  menu.style.top = e.pageY + 'px';
});

document.addEventListener('click', () => {
  document.getElementById('customMenu').style.display = 'none';
});
</script>


二、表单处理类

表单验证增强

<form id="myForm">
  <input type="email" id="email" placeholder="邮箱">
  <div class="error" id="emailError"></div>
  
  <input type="password" id="password" placeholder="密码(6-12位)">
  <div class="error" id="pwdError"></div>

  <button type="submit">提交</button>
</form>

<script>
const form = document.getElementById('myForm');

// 实时验证
document.getElementById('email').addEventListener('input', (e) => {
  const email = e.target.value;
  const regex = /^[^\s@]+@[^\s@]+.[^\s@]+$/;
  document.getElementById('emailError').textContent = 
    regex.test(email) ? '' : '邮箱格式不正确';
});

// 提交验证
form.addEventListener('submit', (e) => {
  e.preventDefault();
  const pwd = document.getElementById('password').value;
  
  if(pwd.length < 6 || pwd.length > 12) {
    document.getElementById('pwdError').textContent = '密码长度需6-12位';
    return;
  }
  
  // 验证通过后提交
  form.submit();
});
</script>

密码强度检测(输入安全相关)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>密码强度检测示例</title>
  <style>
    #password {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
    }
    #strengthMeter {
      width: 100%;
      height: 10px;
      background-color: #ddd;
      border-radius: 5px;
      margin-bottom: 10px;
    }
    #strengthMeterFill {
      height: 100%;
      width: 0;
      border-radius: 5px;
      transition: width 0.3s, background-color 0.3s;
    }
    #strengthText {
      font-size: 14px;
      color: #666;
    }
  </style>
</head>
<body>
  <input type="password" id="password" placeholder="输入密码">
  <div id="strengthMeter">
    <div id="strengthMeterFill"></div>
  </div>
  <div id="strengthText"></div>
  <script>
    document.getElementById('password').addEventListener('input', (e) => {
      const password = e.target.value;
      const meterFill = document.getElementById('strengthMeterFill');
      const strengthText = document.getElementById('strengthText');
      const strength = calculateStrength(password);
      meterFill.style.width = strength + '%';
      meterFill.style.backgroundColor = getStrengthColor(strength);
      strengthText.textContent = getStrengthText(strength);
    });

    function calculateStrength(password) {
      let strength = 0;
      if (password.length >= 8) strength += 25;
      if (/[A-Z]/.test(password)) strength += 25;
      if (/[a-z]/.test(password)) strength += 25;
      if (/[0-9]/.test(password)) strength += 25;
      return strength;
    }

    function getStrengthColor(strength) {
      if (strength < 25) return '#ff0000';
      if (strength < 50) return '#ffaa00';
      if (strength < 75) return '#ffdd00';
      return '#4CAF50';
    }

    function getStrengthText(strength) {
      if (strength < 25) return '弱';
      if (strength < 50) return '一般';
      if (strength < 75) return '强';
      return '非常强';
    }
  </script>
</body>
</html>


三、数据存储类

本地存储记忆功能(localStorage/cookie操作)

        本地存储记忆功能。这需要利用localStorage来保存用户的数据,比如表单输入或主题设置。在页面加载时读取存储的数据,并在数据变化时更新存储。例如,记住用户的搜索历史或表单填写内容。

// 保存数据
const saveData = () => {
  const data = {
    username: document.getElementById('name').value,
    theme: document.body.classList.contains('dark') ? 'dark' : 'light'
  };
  localStorage.setItem('userPrefs', JSON.stringify(data));
};

// 读取数据
window.addEventListener('load', () => {
  const saved = JSON.parse(localStorage.getItem('userPrefs'));
  if(saved) {
    document.getElementById('name').value = saved.username;
    if(saved.theme === 'dark') document.body.classList.add('dark');
  }
});

// 输入时自动保存
document.getElementById('name').addEventListener('input', saveData);


四、动态内容处理

实时搜索过滤

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>实时搜索过滤示例</title>
  <style>
    #searchInput {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
    }
    .item {
      padding: 10px;
      margin-bottom: 5px;
      background-color: #f1f1f1;
    }
  </style>
</head>
<body>
  <input type="text" id="searchInput" placeholder="搜索...">
  <div class="item">项目 1</div>
  <div class="item">项目 2</div>
  <div class="item">项目 3</div>
  <div class="item">项目 4</div>
  <div class="item">项目 5</div>
  <script>
    document.getElementById('searchInput').addEventListener('input', (e) => {
      const searchTerm = e.target.value.toLowerCase();
      document.querySelectorAll('.item').forEach(item => {
        const text = item.textContent.toLowerCase();
        item.style.display = text.includes(searchTerm) ? 'block' : 'none';
      });
    });
  </script>
</body>
</html>

动态加载更多内容

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>动态加载更多内容示例</title>
  <style>
    #loadMore {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
    .item {
      padding: 10px;
      margin-bottom: 5px;
      background-color: #f1f1f1;
    }
  </style>
</head>
<body>
  <div id="content">
    <div class="item">项目 1</div>
    <div class="item">项目 2</div>
    <div class="item">项目 3</div>
  </div>
  <button id="loadMore">加载更多</button>
  <script>
    let currentPage = 1;
    const loadMoreBtn = document.getElementById('loadMore');

    loadMoreBtn.addEventListener('click', () => {
      currentPage++;
      // 模拟数据加载
      setTimeout(() => {
        const data = [`项目 ${currentPage * 3 - 2}`, `项目 ${currentPage * 3 - 1}`, `项目 ${currentPage * 3}`];
        renderItems(data);
      }, 1000);
    });

    function renderItems(data) {
      const content = document.getElementById('content');
      data.forEach(item => {
        const div = document.createElement('div');
        div.className = 'item';
        div.textContent = item;
        content.appendChild(div);
      });
    }
  </script>
</body>
</html>

随机名言生成器

        随机名言生成器需要预定义名言数组,随机选择一条显示,并可能通过按钮点击切换。如果用户需要,可以扩展为从API获取数据,但当前需求是纯JS,所以先使用本地数据。

<div id="quoteDisplay"></div>
<button onclick="newQuote()">新语录</button>

<script>
const quotes = [
  "代码写得好,头发掉得少",
  "Ctrl+C/V 是程序员最高礼仪",
  "程序不工作?试试console.log()",
  "永远相信:下一个版本会更好"
];

function newQuote() {
  const randomIndex = Math.floor(Math.random() * quotes.length);
  document.getElementById('quoteDisplay').textContent = quotes[randomIndex];
}

// 初始化
newQuote();
</script>


五、实用工具类

网页计时器/倒计时

        网页计时器/倒计时需要用到setInterval来更新时间显示,处理开始、暂停和重置功能。需要注意时间的格式化,如补零显示,并在倒计时结束时触发事件。

<div id="timer">00:00:00</div>
<button onclick="startTimer()">开始</button>
<button onclick="pauseTimer()">暂停</button>

<script>
let seconds = 0;
let timerId;

function formatTime(sec) {
  const h = Math.floor(sec/3600).toString().padStart(2,'0');
  const m = Math.floor(sec%3600/60).toString().padStart(2,'0');
  const s = (sec%60).toString().padStart(2,'0');
  return `${h}:${m}:${s}`;
}

function startTimer() {
  if(!timerId) {
    timerId = setInterval(() => {
      seconds++;
      document.getElementById('timer').textContent = formatTime(seconds);
    }, 1000);
  }
}

function pauseTimer() {
  clearInterval(timerId);
  timerId = null;
}
</script>

复制到剪贴板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>复制到剪贴板示例</title>
  <style>
    #copyText {
      width: 100%;
      padding: 10px;
      margin-bottom: 20px;
      background-color: #f1f1f1;
    }
    #copyBtn {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div id="copyText">这是要复制的文本</div>
  <button id="copyBtn">复制到剪贴板</button>
  <script>
    document.getElementById('copyBtn').addEventListener('click', () => {
      const text = document.getElementById('copyText').innerText;
      navigator.clipboard.writeText(text)
        .then(() => {
          alert('已复制!');
        })
        .catch(err => {
          console.error('复制失败:', err);
          // 降级方案
          const textarea = document.createElement('textarea');
          textarea.value = text;
          document.body.appendChild(textarea);
          textarea.select();
          document.execCommand('copy');
          document.body.removeChild(textarea);
          alert('已复制!');
        });
    });
  </script>
</body>
</html>

简易画板功能(图形交互工具)

        简易画板功能需要处理canvas的鼠标事件,跟踪鼠标位置来绘制路径。可能需要设置画笔属性,如颜色和粗细,并实现清除画布的功能。

<canvas id="myCanvas" width="500" height="300"></canvas>
<button onclick="clearCanvas()">清空</button>

<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
let isDrawing = false;

canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);

function startDrawing(e) {
  isDrawing = true;
  ctx.beginPath();
  ctx.moveTo(e.offsetX, e.offsetY);
}

function draw(e) {
  if(!isDrawing) return;
  ctx.lineTo(e.offsetX, e.offsetY);
  ctx.stroke();
}

function stopDrawing() {
  isDrawing = false;
}

function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}
</script>


六、用户体验优化

回到顶部按钮

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>回到顶部按钮示例</title>
  <style>
    body {
      height: 2000px;
    }
    #topBtn {
      position: fixed;
      bottom: 20px;
      right: 20px;
      display: none;
      width: 50px;
      height: 50px;
      background-color: #4CAF50;
      color: white;
      border-radius: 50%;
      text-align: center;
      line-height: 50px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div style="height: 1500px; background-color: #f1f1f1;"></div>
  <button id="topBtn"></button>
  <script>
    window.onscroll = () => {
      document.getElementById('topBtn').style.display = 
        (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) ? 'block' : 'none';
    };

    document.getElementById('topBtn').onclick = () => {
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };

    window.onload = () => {
      document.getElementById('topBtn').style.display = 'none';
    };
  </script>
</body>
</html>

图片懒加载(性能优化)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图片懒加载示例</title>
  <style>
    .lazyload {
      width: 100%;
      height: 200px;
      background-color: #f1f1f1;
      margin: 20px 0;
    }
  </style>
</head>
<body>
  <img data-src="https://via.placeholder.com/800x200" class="lazyload" alt="Lazy-loaded image">
  <img data-src="https://via.placeholder.com/800x200" class="lazyload" alt="Lazy-loaded image">
  <img data-src="https://via.placeholder.com/800x200" class="lazyload" alt="Lazy-loaded image">
  <div style="height: 1000px;"></div>
  <script>
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          observer.unobserve(img);
        }
      });
    });

    document.querySelectorAll('.lazyload').forEach(img => observer.observe(img));
  </script>
</body>
</html>

原生js图片放大镜效果

        图片放大镜效果比较复杂。需要监听鼠标移动事件,计算放大区域的位置和比例,使用canvas来绘制放大后的图像。可能需要处理图片的加载和缩放比例,确保放大镜跟随鼠标移动。

<div class="img-container">
  <img src="small.jpg" id="targetImg">
  <div id="magnifier" style="display:none; width:200px; height:200px; position:absolute"></div>
</div>

<script>
const img = document.getElementById('targetImg');
const magnifier = document.getElementById('magnifier');

img.addEventListener('mousemove', (e) => {
  const rect = img.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  magnifier.style.display = 'block';
  magnifier.style.background = `
    url(${img.src}) 
    no-repeat 
    ${-x*2}px ${-y*2}px
  `;
  magnifier.style.left = e.pageX + 15 + 'px';
  magnifier.style.top = e.pageY + 15 + 'px';
});

img.addEventListener('mouseleave', () => {
  magnifier.style.display = 'none';
});
</script>

暗黑模式切换

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>暗黑模式切换示例</title>
  <style>
    body {
      transition: background-color 0.3s, color 0.3s;
    }
    body.dark-mode {
      background-color: #333;
      color: white;
    }
    #darkModeToggle {
      padding: 10px 20px;
      background-color: #4CAF50;
      color: white;
      border: none;
      border-radius: 5px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <button id="darkModeToggle">切换暗黑模式</button>
  <script>
    const toggle = document.getElementById('darkModeToggle');

    toggle.addEventListener('click', () => {
      document.body.classList.toggle('dark-mode');
      const theme = document.body.classList.contains('dark-mode') ? 'dark' : 'light';
      localStorage.setItem('theme', theme);
    });

    window.onload = () => {
      const savedTheme = localStorage.getItem('theme');
      if (savedTheme === 'dark') {
        document.body.classList.add('dark-mode');
      }
    };
  </script>
</body>
</html>

网页主题色切换

        网页主题色切换可以通过切换CSS类或直接修改样式属性,使用localStorage记住用户选择的主题。需要考虑如何动态改变主题颜色,并在页面加载时应用已保存的主题。

<button onclick="toggleTheme()">切换主题</button>

<script>
function toggleTheme() {
  const root = document.documentElement;
  const isDark = root.classList.toggle('dark-theme');
  
  // 保存主题状态
  localStorage.setItem('theme', isDark ? 'dark' : 'light');
}

// 初始化主题
document.addEventListener('DOMContentLoaded', () => {
  const savedTheme = localStorage.getItem('theme') || 'light';
  document.documentElement.classList.toggle('dark-theme', savedTheme === 'dark');
});

/* CSS需配合:
:root { --bg: #fff; --text: #333; }
.dark-theme { --bg: #222; --text: #fff; }
body { background: var(--bg); color: var(--text); }
*/
</script>


七、视觉效果类

点击切换图片(基础视觉交互)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>产品展示/图片画廊</title>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    .nav {
        width: 80%;
        margin: 0 auto;
        height: 65px;
        display: flex;
    }
    .nav span {
        flex: 1;
        height: 65px;
        text-align: center;
        line-height: 65px;
        font-size: 24px;
        font-weight: bold;
        cursor: pointer; /* 添加鼠标指针样式 */
    }
    .nav .siz {
        background: orange; /* 高亮显示的导航项背景色 */
    }
    .content {
        width: 80%;
        height: 500px;
        margin: 20px auto; /* 调整内容与导航之间的间距 */
        display: flex;
        flex-wrap: wrap; /* 允许内容换行 */
    }
    .content div {
        display: none; /* 默认隐藏所有内容区域 */
        width: 49%; /* 设置每个内容区域的宽度 */
        margin: 0.5%; /* 设置内容区域之间的间距 */
        box-sizing: border-box; /* 包含内边距和边框在内计算元素的总宽度和高度 */
    }
    .content .show {
        display: block; /* 显示当前高亮的内容区域 */
    }
    .content img {
        width: 100%; /* 图片宽度自适应内容区域 */
        height: auto; /* 图片高度自动调整以保持比例 */
    }
</style>
</head>
<body>

<div class="nav"></div>
<div class="content"></div>

<script>
    let car = {
        data: [
            { name: '产品1', pics: ['img1-1.jpg', 'img1-2.jpg'] },
            { name: '产品2', pics: ['img2-1.jpg', 'img2-2.jpg'] },
            // ... 可以继续添加更多产品数据
        ]
    };

    let data = car.data;
    let nav = document.querySelector(".nav");
    let content = document.querySelector(".content");

    // 渲染导航和内容区域
    for (let i = 0; i < data.length; i++) {
        let span = document.createElement("span");
        span.innerText = data[i].name;
        span.onclick = function () {
            // 移除所有span和div的siz和show类
            let navSpans = document.querySelectorAll(".nav span");
            let contentDivs = document.querySelectorAll(".content div");
            navSpans.forEach(span => span.className = '');
            contentDivs.forEach(div => div.className = '');

            // 给当前点击的span和对应的div添加siz和show类
            this.className = 'siz';
            contentDivs[i].className = 'show';
        }
        nav.appendChild(span);

        let div = document.createElement("div");
        let img1 = document.createElement("img");
        let img2 = document.createElement("img"); 
        // 这里虽然创建了两个img元素,但实际上每个div只显示一个(根据需求调整)
        // 为了简化示例,这里只设置第一个img的src,第二个可以隐藏或根据需求处理
        img1.setAttribute("src", data[i].pics[0]);
        // img2.setAttribute("src", data[i].pics[1]); 
        // 如果需要显示两张图片,可以取消注释并调整CSS布局
        div.appendChild(img1);
        // div.appendChild(img2); 
        // 如果需要显示两张图片,可以取消注释
        content.appendChild(div);
    }

    // 设置初始状态:默认显示第一个产品/项目
    let navSpans = document.querySelectorAll(".nav span");
    let contentDivs = document.querySelectorAll(".content div");
    if (navSpans.length > 0 && contentDivs.length > 0) {
        navSpans[0].className = 'siz';
        contentDivs[0].className = 'show';
    }
</script>

</body>
</html>

滚动动画触发

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>滚动动画触发示例</title>
  <style>
    .animate-on-scroll {
      opacity: 0;
      transform: translateY(20px);
      transition: opacity 0.5s, transform 0.5s;
    }
    .animate-on-scroll.active {
      opacity: 1;
      transform: translateY(0);
    }
    .box {
      width: 100%;
      height: 200px;
      background-color: #f1f1f1;
      margin: 20px 0;
      padding: 20px;
      box-sizing: border-box;
    }
    .spacer {
      height: 1000px;
      background-color: #f9f9f9;
    }
  </style>
</head>
<body>
  <div class="spacer"></div>
  <div class="animate-on-scroll box">动画内容 1</div>
  <div class="animate-on-scroll box">动画内容 2</div>
  <div class="animate-on-scroll box">动画内容 3</div>
  <div class="spacer"></div>
  <script>
    window.addEventListener('scroll', () => {
      document.querySelectorAll('.animate-on-scroll').forEach(el => {
        const rect = el.getBoundingClientRect();
        if (rect.top < window.innerHeight * 0.8) {
          el.classList.add('active');
        }
      });
    });
  </script>
</body>
</html>

视差滚动效果

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>视差滚动效果示例</title>
  <style>
    .parallax {
      position: relative;
      height: 500px;
      background-image: url('https://via.placeholder.com/1920x1080');
      background-size: cover;
      background-position: center;
    }
    .content {
      height: 1000px;
      background-color: #f1f1f1;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div class="parallax"></div>
  <div class="content">
    <p>滚动页面查看视差效果。</p>
    <!-- 添加更多内容 -->
  </div>
  <script>
    window.addEventListener('scroll', () => {
      const scrolled = window.pageYOffset;
      document.querySelector('.parallax').style.transform = `translateY(${scrolled * 0.5}px)`;
    });
  </script>
</body>
</html>

此文尚需更新,敬请期待

码字不易,各位大佬点点赞呗

JS进阶-异步编程、跨域、懒加载

作者 咔咔库奇
2025年5月17日 16:29

目录

一、异步编程

1.1.异步编程的基本概念与重要性

1.2.事件循环(Event Loop)机制

1.3.JavaScript异步编程的常见方式及详解

示例

1.4.异步编程的最佳实践

二、跨域

2.1.什么是跨域

2.2.怎么解决跨域

1. JSONP(JSON with Padding)

2. CORS(Cross-Origin Resource Sharing)

3. 反向代理/Nginx反向代理

4. WebSocket

5. postMessage

6. Node.js中间件代理

三、懒加载

1. 懒加载原理

2. 懒加载思路及实现

3. 图片的懒加载

案例


一、异步编程

1.1.异步编程的基本概念与重要性

        异步编程是一种编程方式,它允许程序在等待某些操作(如网络请求、文件读取、定时器等)完成的同时,继续执行其他任务。这种编程方式可以显著提高程序的执行效率,特别是在处理I/O密集型任务时表现出色。

        在JavaScript中,由于它是单线程语言,即同一时间只能执行一个任务,因此异步编程显得尤为重要。如果使用同步编程模型来处理需要大量时间的任务,会阻塞整个线程,导致页面或程序卡顿。而异步编程则可以让程序在执行这些长时间任务时,继续执行其他代码,从而优化用户体验。

1.2.事件循环(Event Loop)机制

1.事件循环(Event Loop)

        JavaScript的执行环境是单线程的,这意味着它同一时间只能执行一个任务。

        事件循环是JavaScript实现异步编程的核心机制。它包含一个执行栈(Call Stack)和一个或多个任务队列(Task Queue)。

        执行栈用于执行同步代码,而任务队列则用于存放异步任务的回调函数。

        当执行栈为空时,事件循环会从任务队列中取出任务并执行。

2.任务类型

宏任务(Macro Task) :如setTimeoutsetInterval、I/O操作等。

微任务(Micro Task) :如Promise的回调、process.nextTick等。

        微任务的优先级高于宏任务。在每一次事件循环中,执行栈中的任务执行完毕后,会先检查微任务队列,执行所有微任务,再执行下一个宏任务。

1.3.JavaScript异步编程的常见方式及详解

1、回调函数(Callback Functions)

                回调函数是最早的异步处理方式。它通过将一个函数作为参数传递给另一个函数,在异步任务完成后调用该函数来处理结果。

        回调函数的优点是简单易懂,但在处理多个异步任务时,会导致回调函数嵌套过多,形成“回调地狱”,使代码难以维护和理解。

2、Promise对象

        Promise是ES6引入的一种异步编程解决方案,它是一个表示未来某个事件(通常是一个异步操作)的结果的对象。

        Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。状态一旦改变就不能再变。

        Promise对象提供了then()catch()方法来处理异步操作的结果和异常。then()方法用于处理成功的情况,catch()方法用于处理失败的情况。

        Promise支持链式调用,可以避免回调地狱,使代码更加简洁易读。

        Promise.all()和Promise.race()是Promise对象的两个静态方法。

        Promise.all()接受一个包含多个Promise的数组,并返回一个新的Promise。当所有Promise都完成时,这个Promise才会完成;如果其中有一个Promise失败,Promise.all()会立即失败。

        Promise.race()则只要数组中的任意一个Promise完成或失败,就会立即完成或失败。

        有关promise的更详细讲解在ES6基础知识https://blog.csdn.net/m0_64455070/article/details/143714359

3、async/await

        async/await是基于Promise的语法糖,是ES2017引入的用于处理异步代码的方式。

  async用来声明一个异步函数,该函数会隐式地返回一个Promise。

  await只能在async函数内部使用,用于等待一个Promise的完成。它返回的是Promise成功的结果值,如果Promise失败,则抛出异常。

        async/await使得异步代码看起来像是同步的,提高了代码的可读性和可维护性。它避免了回调地狱,并且错误处理更加方便。

        有关async/await更详细讲解在本文作者的ES6基础知识https://blog.csdn.net/m0_64455070/article/details/143714359

示例

以下是一个使用async/await实现异步网络请求的示例代码:

async function fetchData(url) {
  try {
    const response = await fetch(url); // 等待fetch函数的Promise完成
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    const data = await response.json(); // 等待响应的JSON数据解析完成
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error; // 将错误抛出给调用者处理
  }
}

// 使用 async/await 处理异步请求结果
(async () => {
  try {
    const data = await fetchData('https://api.example.com/data');
    console.log('Request succeeded:', data);
  } catch (error) {
    console.error('Request failed:', error);
  }
})();

在这个示例中,fetchData函数是一个异步函数,它使用await关键字等待fetch函数的Promise完成,并获取响应的JSON数据。然后,我们使用一个立即执行的异步函数来调用fetchData函数,并处理异步请求的结果。如果请求失败,则捕获异常并打印错误信息。

1.4.异步编程的最佳实践

避免回调地狱:使用Promise或async/await来避免回调地狱,使代码更加简洁易读。

错误处理:在异步代码中添加适当的错误处理逻辑,以确保程序的健壮性。

代码可读性:使用有意义的函数名和变量名,以及适当的注释来提高代码的可读性。

性能优化:避免不必要的异步操作,以减少资源消耗和提高程序性能。

二、跨域

2.1.什么是跨域

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。在Web开发中,由于同源策略(Same-Origin Policy)的限制,浏览器默认不允许跨域请求。如果尝试进行跨域请求,浏览器会抛出安全错误。

什么是同源策略?

同源策略是一种安全功能,它要求协议、域名和端口三者必须相同,才允许访问资源。

2.2.怎么解决跨域

1. JSONP(JSON with Padding)

原理:

JSONP是一种通过<script>标签实现跨域请求的技术。由于<script>标签不受同源策略的限制,可以加载其他域的JavaScript文件。

优点:

兼容性好,可以解决主流浏览器的跨域问题

缺点:

仅支持GET请求。

不安全,可能遭受XSS攻击。

实现步骤

JSONP的工作流程如下:

前端:动态创建一个<script>标签,并设置其src属性为跨域的URL,同时在URL中传入一个回调函数名(如callback=myFunction)。

后端:服务器接收到请求后,将返回的数据作为回调函数的参数,并包装成JavaScript代码返回(如myFunction({"key":"value"}))。

前端:当浏览器加载这段JavaScript代码时,会自动调用回调函数myFunction,从而实现跨域数据传输。

但JSONP只能发送GET请求,且存在安全风险,容易受到XSS攻击。

2. CORS(Cross-Origin Resource Sharing)

原理

CORS是一种由服务器设置响应头来允许跨域请求的机制。服务器通过设置特定的HTTP响应头,如Access-Control-Allow-Origin,来指定哪些域名或IP地址可以跨域访问资源。

优点

灵活,可以细粒度地控制哪些源可以访问资源。

前端无需配置,只需后端设置响应头。

缺点

需要服务器支持

实现步骤

前端:无需特殊配置,只需发送跨域请求即可。

后端:服务器需要在响应头中设置Access-Control-Allow-Origin等CORS相关的头部字段。

CORS支持所有类型的HTTP请求,且安全性较高,是跨域请求的推荐解决方案。但需要注意的是,CORS需要服务器和浏览器同时支持才能实现。

3. 反向代理/Nginx反向代理

原理

反向代理是一种在服务器端设置代理服务器,将前端的跨域请求转发到目标服务器,并将目标服务器的响应返回给前端的技术。Nginx是一种常见的反向代理服务器。

优点

解决跨域问题:通过反向代理,前端可以与目标服务器进行跨域通信,而无需修改前端代码。

安全性:反向代理可以作为一道安全屏障,隐藏目标服务器的真实地址,防止直接攻击。

负载均衡:在大型应用中,反向代理可以将请求分发到多个目标服务器,实现负载均衡,提高系统的可用性和性能。

缓存:反向代理可以缓存目标服务器的响应,减少对目标服务器的请求次数,提高响应速度。

缺点

配置复杂性:反向代理需要服务器端的配置和管理,这可能需要一定的技术知识和经验。

单点故障:如果反向代理服务器出现故障,整个系统可能会受到影响,导致服务中断。

性能瓶颈:虽然反向代理可以提高性能,但在高并发场景下,它也可能成为性能瓶颈。

实现步骤

前端:无需特殊配置,只需发送请求到反向代理服务器即可。

后端:需要在服务器端(如Nginx)配置反向代理规则,将请求转发到目标服务器。

通过反向代理,前端可以认为是在与同源服务器通信,从而绕过浏览器的同源策略限制。但需要注意的是,反向代理需要服务器端的配置和管理。

4. WebSocket

原理

WebSocket是一种在单个TCP连接上进行全双工通讯的协议。它默认支持跨域请求,可以在客户端和服务器之间建立持久的连接,并通过该连接进行实时数据传输。

优点

支持全双工通信,实时性好。

不受同源策略限制。

缺点

需要服务器支持WebSocket协议。

可能存在性能问题,如页面建立多个WebSocket连接。

实现步骤

前端:使用WebSocket API(如new WebSocket('ws://example.com/socket'))建立连接。

后端:服务器需要支持WebSocket协议,并处理连接和数据传输。

WebSocket适用于需要实时通信或双向通信的场景,如聊天室、实时数据监控等。

5. postMessage

原理

postMessage是HTML5中提供的一个API,用于在不同窗口、标签页或iframe之间进行跨域通信。

优点

安全,可以通过origin参数控制消息的来源。

支持复杂的通信场景,如跨域iframe之间的通信。

缺点

需要双方配合实现。

实现步骤

前端:使用window.postMessage方法发送消息到目标窗口或iframe。

接收端:目标窗口或iframe需要监听message事件,并处理接收到的消息。

postMessage适用于不同窗口之间的通信,如父子窗口、跨域iframe等。但需要在接收端进行安全验证以防止恶意攻击。

6. Node.js中间件代理

原理

当前端发送跨域请求到Node.js服务器时,Node.js服务器使用中间件拦截并处理这些请求。中间件可以检查请求的头信息、参数等,并根据配置将请求转发到目标服务器。目标服务器处理请求后,将响应返回给Node.js服务器,Node.js服务器再将响应转发给前端。通过这种方式,Node.js服务器作为中间层,实现了跨域通信。常见的中间件有corshttp-proxy-middleware等。

优点

灵活性:Node.js中间件代理可以灵活地处理跨域请求,并与其他Node.js应用集成。

易于配置:与Nginx等反向代理服务器相比,Node.js中间件代理的配置通常更加简单和直观。

可扩展性:Node.js中间件代理可以轻松地与其他中间件和路由集成,实现更复杂的功能。

缺点

性能:与Nginx等高性能反向代理服务器相比,Node.js中间件代理的性能可能稍逊一筹。

资源消耗:Node.js中间件代理需要额外的资源来运行和处理请求,这可能会增加服务器的负载。

维护成本:与Nginx等成熟的反向代理服务器相比,Node.js中间件代理可能需要更多的维护和支持

实现步骤

前端:发送请求到Node.js服务器。

后端:Node.js服务器使用中间件拦截并处理跨域请求,将请求转发到目标服务器,并将响应返回给前端。

Node.js中间件代理需要服务器端的配置和管理,但可以灵活地处理跨域请求,并与其他Node.js应用集成。

三、懒加载

1. 懒加载原理

        懒加载,即按需加载或延迟加载,是指当页面或应用中的某些资源(如图片、视频、数据等)在需要时才进行加载,而不是在页面初始化时一次性加载所有资源。这种技术的核心原理是减少初始加载时间和网络流量,提高页面响应速度和用户体验。

懒加载的原理主要基于以下几点:

按需加载:只加载用户当前需要或即将需要的资源,避免加载无用资源。

异步加载:将资源的加载推迟到用户需要使用时再进行,不阻塞用户界面的渲染。

动态加载:根据用户的行为和需求,动态地生成和加载页面内容或资源。

2. 懒加载思路及实现

  1. 确定需要懒加载的资源:根据页面或应用的需求,确定哪些资源可以或应该进行懒加载。
  2. 设置占位符:在资源实际加载之前,使用占位符(如低分辨率图片、默认图标等)来占据资源的位置。
  3. 监听用户行为:通过事件监听(如滚动事件、点击事件等)来检测用户何时需要加载资源。
  4. 加载资源:当用户需要资源时,触发加载逻辑,从服务器获取资源并替换占位符。

懒加载的实现方式有多种,包括但不限于:

  1. 图片懒加载:通过监听滚动事件和判断图片是否进入可视区域来实现图片的延迟加载。
  2. 视频懒加载:在视频播放器进入用户视野或用户点击播放按钮时才开始加载视频内容。
  3. 数据懒加载:在用户滚动到页面底部或进行分页操作时,加载更多的数据。

3. 图片的懒加载

图片的懒加载是懒加载技术中最常见的应用之一。

步骤

在HTML中为需要懒加载的图片设置占位符,并使用自定义属性(如data-src)存储真实图片的路径。

在JavaScript中监听滚动事件,判断图片是否进入可视区域。

当图片进入可视区域时,将占位符替换为真实图片

// 获取页面中的所有懒加载图片
var imgs = document.querySelectorAll('img[data-src]');

// 监听滚动事件
window.addEventListener('scroll', function() {
    // 遍历所有懒加载图片
    imgs.forEach(function(img) {
        // 判断图片是否进入可视区域
        if (isImageInViewport(img)) {
            // 替换占位符为真实图片
            img.src = img.getAttribute('data-src');
            // 移除data-src属性,避免重复加载
            img.removeAttribute('data-src');
        }
    });
});

// 判断图片是否进入可视区域的函数
function isImageInViewport(img) {
    var rect = img.getBoundingClientRect();
    var inViewport = (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
    return inViewport;
}

现代浏览器支持

现代浏览器(如Chrome、Firefox、Safari等)通常支持图片的懒加载属性。可以在<img>标签中使用loading="lazy"属性来告诉浏览器延迟加载图片,直到它们出现在视口中。

例如:<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="Description">

案例

1. 图片懒加载(使用JavaScript和Intersection Observer API)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Lazy Load Example</title>
<style>
  .lazy-load {
    opacity: 0;
    transition: opacity 0.3s;
  }
  .lazy-load.loaded {
    opacity: 1;
  }
</style>
</head>
<body>

<!-- 图片列表,使用 data-src 属性存储真实图片 URL -->
<img class="lazy-load" data-src="image1.jpg" alt="Image 1">
<img class="lazy-load" data-src="image2.jpg" alt="Image 2">
<img class="lazy-load" data-src="image3.jpg" alt="Image 3">
<!-- ...更多图片... -->

<script>
  // 获取所有需要懒加载的图片
  const lazyImages = document.querySelectorAll('.lazy-load');

  // 创建 Intersection Observer 实例
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        // 当图片进入可视区域时,加载图片
        const img = entry.target;
        img.src = img.dataset.src;
        img.classList.add('loaded');
        // 停止观察这个图片
        observer.unobserve(img);
      }
    });
  });

  // 开始观察每个图片
  lazyImages.forEach(img => {
    observer.observe(img);
  });
</script>

</body>
</html>

  1. 视频懒加载(使用JavaScript和事件监听)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Video Lazy Load Example</title>
</head>
<body>

<!-- 视频列表,使用 data-src 属性存储视频 URL -->
<div class="video-container">
  <video class="lazy-load" controls>
    <source data-src="video1.mp4" type="video/mp4">
    Your browser does not support the video tag.
  </video>
</div>
<div class="video-container">
  <video class="lazy-load" controls>
    <source data-src="video2.mp4" type="video/mp4">
    Your browser does not support the video tag.
  </video>
</div>
<!-- ...更多视频... -->

<script>
  // 获取所有需要懒加载的视频
  const lazyVideos = document.querySelectorAll('.lazy-load video');

  // 为每个视频容器添加点击事件监听器
  lazyVideos.forEach(videoContainer => {
    const video = videoContainer.querySelector('video');
    const source = video.querySelector('source');

    videoContainer.addEventListener('click', () => {
      // 当视频容器被点击时,加载视频
      video.src = source.dataset.src;
      video.load(); // 触发浏览器加载视频资源
      // 移除 data-src 属性,避免重复加载
      source.removeAttribute('data-src');
      // 移除点击事件监听器,避免重复操作
      videoContainer.removeEventListener('click', arguments.callee);
    });
  });
</script>

</body>
</html>

  1. 数据懒加载(使用JavaScript和滚动事件监听)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Lazy Load Example</title>
<style>
  #content {
    max-height: 400px;
    overflow-y: auto;
  }
  .item {
    padding: 10px;
    border-bottom: 1px solid #ccc;
  }
</style>
</head>
<body>

<div id="content">
  <!-- 初始内容 -->
  <div class="item">Item 1</div>
  <div class="item">Item 2</div>
  <!-- ...更多初始内容... -->

  <!-- 占位符,用于加载更多内容 -->
  <div id="load-more">Load More</div>
</div>

<script>
  let currentPage = 1; // 当前页码

  // 为“加载更多”按钮添加点击事件监听器
  document.getElementById('load-more').addEventListener('click', () => {
    loadMoreData(currentPage);
    currentPage++; // 更新页码
  });

  // 滚动事件监听器,当用户滚动到底部时自动加载更多数据
  window.addEventListener('scroll', () => {
    const content = document.getElementById('content');
    const loadMore = document.getElementById('load-more');
    const bottomOfWindow = window.scrollY + window.innerHeight === document.body.offsetHeight;
    const bottomOfContent = content.scrollTop + content.clientHeight >= content.scrollHeight - 10; // 减去10是为了避免因为滚动条或内容高度计算误差导致的重复加载

    if (bottomOfWindow || bottomOfContent) {
      loadMoreData(currentPage);
      currentPage++; // 更新页码
      // 可以选择隐藏或禁用“加载更多”按钮,直到新数据加载完成
      loadMore.style.display = 'none';
    }
  });

  // 模拟加载更多数据的函数
  function loadMoreData(page) {
    setTimeout(() => {
      const content = document.getElementById('content');
      const loadMore = document.getElementById('load-more');

      // 清除之前的占位符(如果存在)
      if (loadMore.style.display === 'none') {
        loadMore.style.display = 'block';
      }

      // 创建新的内容项并添加到页面中
      for (let i = 0; i < 5; i++) {
        const newItem = document.createElement('div');
        newItem.className = 'item';
        newItem.textContent = `Item ${(page - 1) * 5 + i + 3}`; // 假设每页加载5个项,从Item 3开始
        content.appendChild(newItem);
      }
    }, 1000); // 模拟网络延迟,设置为1秒
  }
</script>

</body>
</html>

【CSS问题】margin塌陷

作者 咔咔库奇
2025年5月17日 10:34

目录

一、什么是css margin塌陷

二、margin塌陷的原因

三、塌陷的深入机制

四、解决margin塌陷的方法

1、避免同级元素margin重叠:

2、解决父子元素粘连:

五、注意事项


一、什么是css margin塌陷

        CSS中的margin塌陷(也称为margin collapsing)是一个常见的布局问题,主要发生在垂直方向上。当两个或多个元素的垂直margin相遇时,它们不会按照预期叠加,而是会发生重叠,导致最终的外边距值比单独设置时小。

二、margin塌陷的原因

  1. 同级元素:两个同级的元素,垂直排列,上面的盒子给margin-bottom,下面的盒子给margin-top,那么他们两个间距就会重叠,以大的那个盒子的外边距计算。
  2. 父子元素:子元素给一个margin-top,其父级元素也会受到影响,同时产生上边距,父子元素会进行粘连。
  3. 示例:设想页面上有一个蓝色的矩形(.parent),它内部有一个粉色的矩形(.child)。由于margin塌陷,.child元素的margin-top实际上导致了整个.parent元素向下移动,使得.parent的顶部与页面顶部之间的间距增加了,而不是.child元素内部增加了间距。     
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Margin Collapse Example</title>
    <style>
        .parent {
            width: 200px;
            height: 200px;
            background-color: blue;
            margin-top: 50px;
        }
        .child {
            width: 100px;
            height: 100px;
            background-color: pink;
            margin-top: 30px; /* 这个边距将与父级边距一起消失 */
        }
    </style>
</head>
<body>
    <div class="parent">
        <div class="child"></div>
    </div>
</body>
</html>

在上述示例中,.child元素的margin-top与.parent元素的margin-top发生了塌陷,导致整个.parent元素相对于页面顶部移动了50px,而不是80px。

三、塌陷的深入机制

  • 合并规则‌:当两个垂直方向上的外边距相遇时,它们会按照特定的规则合并。对于同级元素,取两者中较大的值作为合并后的外边距;对于父子元素,子元素的margin-top会与父元素的margin-top合并,导致整个父元素向下移动。
  • BFC(块级格式化上下文) ‌:BFC是一个独立的渲染区域,只有属于同一个BFC的元素才会发生外边距合并。触发BFC的方法包括设置overflow属性为hiddenautoscroll,将display属性设置为table-cellinline-blockflex等,或者将float属性设置为leftright等。
  • 包含块‌:每个元素都有一个包含块,它决定了元素的定位和大小。在margin塌陷的情况下,子元素的margin-top实际上是与父元素的包含块顶部对齐,而不是与父元素的内部内容对齐。

四、解决margin塌陷的方法

1、避免同级元素margin重叠

可以使两个外边距不同时出现,即要么只设置上面的margin-bottom,要么只设置下面的margin-top

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Avoid Margin Overlap Example</title>
<style>
  .sibling1 {
    background-color: lightcoral;
    margin-bottom: 20px; /* 只设置下面的margin */
  }
  .sibling2 {
    background-color: lightseagreen;
    /* 不设置margin-top,避免与上面的margin重叠 */
  }
</style>
</head>
<body>
  <div class="sibling1">Sibling 1</div>
  <div class="sibling2">Sibling 2</div>
</body>
</html>

2、解决父子元素粘连

为父盒子设置border:为外层添加border后父子盒子就不是真正意义上的贴合,可以设置透明边框(border:1px solid transparent;)。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Solve Parent-Child Adhesion Example</title>
<style>
  .parent {
    background-color: lightblue;
    border: 1px solid transparent; /* 设置透明边框以避免粘连 */
  }
  .child {
    background-color: coral;
    margin-top: 30px;
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

为父盒子添加overflow:hidden:这样可以触发父盒子的块级格式化上下文(BFC),从而避免margin塌陷。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin Collapse Example</title>
<style>
  .parent {
    background-color: lightblue;
    display: flex; /* 改变display属性以触发BFC */
  }
  .child {
    background-color: coral;
    margin-top: 30px;
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

为父盒子设置padding值:通过给父元素添加内边距,可以使得子元素的margin不再与父元素的顶部粘连。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin Collapse Example</title>
<style>
  .parent {
    background-color: lightblue;
    padding-top: 20px; /* 添加内边距以避免margin塌陷 */
  }
  .child {
    background-color: coral;
    margin-top: 30px;
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

改变父盒子的display属性:如设置为display:table;display:flex;等,都可以触发BFC,从而解决margin塌陷问题。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin Collapse Example</title>
<style>
  .parent {
    background-color: lightblue;
    display: flex; /* 改变display属性以触发BFC */
  }
  .child {
    background-color: coral;
    margin-top: 30px;
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

利用伪元素:给父元素的前面添加一个空元素,并设置该伪元素的样式以避免margin塌陷。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin Collapse Example</title>
<style>
  .parent::before {
    content: "";
    display: block;
    height: 0; /* 创建一个不可见的块级元素 */
  }
  .parent {
    background-color: lightblue;
  }
  .child {
    background-color: coral;
    margin-top: 30px;
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

改变子元素的定位:如设置为position:absolute;,这样子元素就不再相对于父元素定位,而是相对于最近的已定位祖先元素定位(如果没有,则相对于初始包含块定位)。

示例:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Margin Collapse Example</title>
<style>
  .parent {
    background-color: lightblue;
  }
  .child {
    background-color: coral;
    margin-top: 30px;
    position: absolute; /* 改变定位方式 */
    top: 20px; /* 调整位置 */
  }
</style>
</head>
<body>
  <div class="parent">
    <div class="child">Child Element</div>
  </div>
</body>
</html>

3、触发BFC的其他方法

  • 将元素的display属性设置为inline-blocktable-celltable-captionflow-rootflexinline-flexgridinline-grid等。
  • 将元素的float属性设置为leftright等(非none)。
  • 将元素的position属性设置为absolutefixed

五、注意事项

1、在解决margin塌陷问题时,需要根据具体的布局需求和元素关系来选择合适的方法。

2、触发BFC是解决margin塌陷的一种有效手段,但需要注意BFC对布局的其他可能影响。

码字不易,觉得有用的话,各位大佬就点点赞呗

CSS基础知识05(弹性盒子、布局详解,动画,3D转换,calc)

作者 咔咔库奇
2025年5月17日 10:32

0、弹性盒子、布局

弹性盒子(Flex Box)是CSS3引入的一种新的布局模式,旨在提供一种更有效的方式来布局、对齐和分配在容器中项目的空间,即使它们的大小未知或是动态变化的。以下是对弹性盒子的超级详解:

0.1.弹性盒子的基本概念

弹性容器(Flex Container) :通过设置display属性的值为flexinline-flex,将一个元素定义为弹性容器。弹性容器内包含了一个或多个弹性子元素。

弹性子元素(Flex Items) :弹性容器的子元素。弹性子元素在弹性容器内按照弹性盒子的规则进行布局。

0.2.弹性盒子的主轴和交叉轴

主轴(Main Axis) :弹性元素排列的主要方向,可以是水平方向或垂直方向。该轴的开始和结束被称为main start和main end。

交叉轴(Cross Axis) :垂直于主轴的方向。该轴的开始和结束被称为cross start和cross end。

0.3.弹性盒子的属性

没有添加弹性盒子的属性

编辑

<html>
<head>
<style>
.box {
width: 1000px;
height: 1000px;

}

.b1 {
width: 100px;
height: 100px;
background-color: red;
}

.b2 {
width: 100px;
height: 100px;
background-color: blue;
}

.b3 {
width: 100px;
height: 100px;
background-color: green;
}

.b4 {
width: 100px;
height: 100px;
background-color: yellow;
}
</style>
</head>
<body>
<body>
<div class="box">
<div class="b1">1</div>
<div class="b2">2</div>
<div class="b3">3</div>
<div class="b4">4</div>
</div>
</body>

</body>
</html>

flex-direction

设置弹性子元素的排列方向。

row

row:从左到右水平排列(默认值)。

编辑

.box {
width: 1000px;
    height: 1000px;
display: flex;
    flex-direction: row;
}

row-reverse

row-reverse:从右到左水平排列。

编辑

.box {
width: 1000px;
    height: 1000px;
display: flex;
    flex-direction: row-reverse;
}

column

column:从上到下垂直排列。

编辑

        .box{
            width: 1000px;
            height: 1000px;
            display: flex;
            flex-direction: column;
        }

column-reverse

column-reverse:从下到上垂直排列。

编辑

        .box{
            width: 1000px;
            height: 1000px;
            display: flex;
            flex-direction: column-reverse;
        }

flex-wrap

设置弹性子元素是否换行。

nowrap

nowrap:不换行(默认值)。

如果所有子元素的宽/高总值大于父元素的宽/高,那么为了子元素不溢出,会把内容挤压变形到自适应的宽高。

编辑

.box {
    width: 300px;
    height: 200px;
    display: flex;
    flex-wrap: nowrap;
}

wrap

wrap:换行。

编辑

.box {
    width: 300px;
    height: 200px;
    display: flex;
    flex-wrap: nowrap;
}

wrap-reverse

wrap-reverse:反向换行。

编辑

flex-dirction和flex-wrap的组合简写模式
.box{
    width: 300px;
    height: 200px;
    display: flex;
    flex-flow: row wrap;
}

编辑

justify-content

定义弹性子元素在主轴上的对齐方式。

flex-start

flex-start:靠主轴起点对齐。

编辑

.box{
    width: 500px;
    height: 200px;
    border: 1px solid black;

    display: flex;
    justify-content: flex-start;
    
}

flex-end

flex-end:靠主轴终点对齐。

编辑

.box{
    width: 500px;
    height: 200px;
    display: flex;
    justify-content: flex-end;
    border: 1px solid black;
}

center

center:居中对齐。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
justify-content: center;
border: 1px solid black;
}

space-between

space-between:两端对齐,元素之间的间隔相等。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
justify-content: space-between;
border: 1px solid black;
}

space-around

space-around:元素两侧的间距相同,元素之间的间距比两侧的间距大一倍。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
justify-content: space-around;
border: 1px solid black;
}

space-evenly

space-evenly:元素间距离平均分配。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
justify-content: space-evenly;
border: 1px solid black;
}

align-items(单行)

定义弹性子元素在交叉轴上的对齐方式(适用于单行)。

flex-start

flex-start:交叉轴起点对齐。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
align-items: flex-start;
border: 1px solid black;
}

flex-end

flex-end:交叉轴终点对齐。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
align-items: flex-end;
border: 1px solid black;
}

center

center:交叉轴中点对齐。

编辑

.box{
width: 500px;
height: 200px;
display: flex;
align-items: center;
border: 1px solid black;
}

baseline

baseline:元素的第一行文字的基线对齐。

改变了每个盒子字体的大小这样看基线比较直观

编辑

.box{
width: 500px;
height: 200px;
display: flex;
align-items: baseline;
border: 1px solid black;
}

stretch

stretch:默认值,如果元素未设置高度或者为auto,将占满整个容器的高度。

红盒子不设置高度,容器的填充方向是按照侧轴方向填充的

编辑

.box{
width: 500px;
height: 200px;
display: flex;
align-items: stretch;
border: 1px solid black;
}

align-content(多行)

定义多行弹性子元素在交叉轴上的对齐方式(适用于多行)。

flex-start

flex-start:交叉轴的起点对齐。

编辑

.box{
width: 500px;
height: 600px;
display: flex;
align-content: flex-start;
flex-wrap: wrap;
border: 1px solid black;
}

flex-end

flex-end:交叉轴的终点对齐。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: flex-end;
flex-wrap: wrap;
border: 1px solid black;
}

center

center:交叉轴的中点对齐。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: center;
flex-wrap: wrap;
border: 1px solid black;
}

space-between

space-between:交叉轴两端对齐之间间隔平分。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: space-between;
flex-wrap: wrap;
border: 1px solid black;
}

space-around

space-around:元素两侧的间距相同,元素之间的间距比两侧的间距大一倍。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: space-around;
flex-wrap: wrap;
border: 1px solid black;
}

stretch

stretch:默认值,轴线占满整个交叉轴。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: stretch;
flex-wrap: wrap;
border: 1px solid black;
}

space-evenly

space-evenly:在交叉轴上平均分配空间。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: space-evenly;
flex-wrap: wrap;
border: 1px solid black;
}

align-self

允许单个弹性子元素有与其他子元素不同的对齐方式,可覆盖align-items属性。

auto

auto:默认值,表示继承父容器的align-items属性。如果没有父元素,则等同于stretch

其他值(如flex-startflex-endcenterbaselinestretch)与align-items相同。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
border: 1px solid black;
}
/*6号盒子靠底部*/
.b6 {
    align-self: flex-end;
    width: 100px;
    height: 100px;
    background-color: aquamarine;
}
/*7号盒子单行居中*/
.b7 {
    align-self: center;
    width: 100px;
    height: 100px;
    background-color: #9317ff;
}

order

order:设置弹性子元素的排列顺序(数值越小,排列越靠前;默认为0)。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: stretch;
flex-wrap: wrap;
border: 1px solid black;
}

.b1 {
width: 100px;
height: 100px;
background-color: red;
order: 3;
}

.b2 {
width: 100px;
height: 100px;
background-color: blue;
order: 1;
}

.b3 {
width: 100px;
height: 100px;
background-color: green;
order: 0;
}
.b4 {
width: 100px;
height: 100px;
background-color: yellow;
order: 2;
}

flex-grow

flex-grow:定义弹性子元素的伸展系数(默认值为0,即如果存在剩余空间,也不伸展)。

编辑

.box{
width: 500px;
height: 300px;
display: flex;
align-content: stretch;
flex-wrap: wrap;
border: 1px solid black;
}

.b1 {
width: 100px;
height: 100px;
background-color: red;
flex-grow: 3;
}

.b2 {
width: 100px;
height: 100px;
background-color: blue;
flex-grow: 1;
}

.b3 {
width: 100px;
height: 100px;
background-color: green;
}
.b4 {
width: 100px;
height: 100px;
background-color: yellow;
}

flex-shrink

flex-shrink:定义了弹性子元素的收缩系数(默认值为1,即如果空间不足,该项目将等比例缩小)。

flex-basis

flex-basis:定义弹性子元素在主轴上的基础长度(默认值auto,即项目的本来大小)。

该属性可以单独使用,也可以与flex-growflex-shrink一起使用,简写为flex属性(如flex: 1 1 auto)。

0.4.弹性盒子的使用场景

响应式布局:弹性盒子可以根据容器的大小和内容的变化自动调整布局,使得页面在不同的屏幕尺寸和设备上都能够适应。

复杂的布局:弹性盒子提供了多种对齐和分布元素的方式,可以方便地实现复杂的布局。

简化代码:与浮动布局相比,弹性布局减少了代码量和复杂度,提高了可读性和可维护性。

一、动画

1.1.动画序列与关键帧

1.@keyframes:

定义动画的关键帧,通过指定0%到100%之间的百分比来定义动画的各个阶段

在每个关键帧中,可以设置元素的样式属性,如位置、大小、颜色等。

/* 创建关键帧动画 动画名字叫one */
@keyframes one {
  /* 从什么到什么 */
  /* 0% */
  from {}
  /* 100% */
  to {}
}

1.2.动画属性详解

1.animation-name:

指定要应用于元素的动画名称,该名称必须与@keyframes中定义的动画名称相匹配

2.animation-duration

定义动画的持续时间,即动画从开始到结束所需要的时间。

单位可以是秒或毫秒(ms)

3.animation-timing-function:

控制动画的缓冲行为,即动画的速度曲线

属性值:ease(逐渐慢下来)、linear(匀速)、ease-in(加速)、ease-out(减速)等,还可以使用cubic-bezier函数指定以速度曲线

4.animation-delay:

指定动画开始前的延迟时间

单位:m或者ms

5.animation-iteration-count:
指定动画应该播放的次数

属性值:数字表示播放次数,infinite表示无限次播放

6.animation-direction

指定动画的播放方向。

属性值:normal(正常)、reverse(反向)、alternate(奇数次正向播放,偶数次反向播放)等。

7.animation-fill-mode

定义动画在开始或结束时元素的状态。

属性值:none(不改变元素状态)、forwards(动画结束时保持最后一帧的状态)、backwards(动画开始前保持第一帧的状态)等。

8.animation-play-state

控制动画的播放状态。

属性值:running(播放)和paused(暂停)。

1.3.动画简写

上述动画属性可简写为一个属性

animation: name duration timing-function delay iteration-count direction fill-mode;

1.4.使用动画库

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<link rel="stylesheet" href="./iconfont.css">
<style>
  h1 {
    animation-iteration-count: infinite;
  }
</style>
<h1 class="animate__animated animate_bounce">我是动画库</h1>
<!-- animate_ _animated基础类名-->
<!-- animate_bounce动画效果-->

1.5.动画事件

使用JavaScript可以监听动画的开始(animationstart)、结束(animationend)、迭代(animationiteration)等事件。

例:

html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Animation Events</title>
    <style>
        .animated-box {
            width: 100px;
            height: 100px;
            background-color: red;
            position: relative;
            animation: moveBox 2s infinite;
        }

        @keyframes moveBox {
            0% { left: 0; }
            50% { left: 200px; }
            100% { left: 0; }
        }
    </style>
</head>
<body>
    <div class="animated-box" id="box"></div>

    <script src="script.js"></script>
</body>
</html>

js文件

// 获取动画元素
const box = document.getElementById('box');

// 监听动画开始事件
box.addEventListener('animationstart', function(event) {
    console.log('Animation started:', event.animationName);
    // 可以在这里添加额外的逻辑,比如改变背景颜色
    box.style.backgroundColor = 'blue';
});

// 监听动画结束事件
box.addEventListener('animationend', function(event) {
    console.log('Animation ended:', event.animationName);
    // 可以在这里添加额外的逻辑,比如重置背景颜色
    box.style.backgroundColor = 'red';
});

// 监听动画迭代事件
box.addEventListener('animationiteration', function(event) {
    console.log('Animation iterated:', event.animationName, 'Iteration count:', event.iterationCount);
    // 可以在这里添加额外的逻辑,比如改变透明度
    box.style.opacity = '0.5';
    // 可以设置一个延时来恢复透明度,避免一直半透明
    setTimeout(() => {
        box.style.opacity = '1';
    }, 100); // 延时100毫秒
});

二、CSS3 3D转换

CSS3的3D转换属性允许开发者对网页元素进行3D空间中的移动、旋转、缩放等操作。

2.1.  3D变换函数

translate3d(tx, ty, tz) :在3D空间中移动元素。

rotate3d(x, y, z, angle) :围绕3D空间中的某个轴旋转元素。

scale3d(sx, sy, sz) :在3D空间中缩放元素。

perspective(d) :为3D元素添加透视效果,使元素看起来更加立体。通常应用于父元素。

transform-style:控制子元素是否开启三维立体环境,如flat(默认,不开启3D立体空间)、preserve-3d(开启3D立体空间)。

2.2. 3D变换与动画结合

可以将3D变换与动画属性结合使用,创建复杂的3D动画效果。

例如,使用rotate3danimation属性创建一个旋转的3D立方体。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Cube Animation</title>
<style>
  body {
    margin: 0;
    perspective: 1000px; /* 为整个场景添加透视效果 */
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #f0f0f0;
  }
  .scene {
    width: 200px;
    height: 200px;
    position: relative;
    transform-style: preserve-3d; /* 开启3D立体空间 */
    animation: rotateCube 5s infinite linear; /* 应用旋转动画 */
  }
  .cube {
    width: 100%;
    height: 100%;
    position: absolute;
    transform-style: preserve-3d; /* 开启3D立体空间 */
  }
  .face {
    position: absolute;
    width: 200px;
    height: 200px;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid #ccc;
    box-sizing: border-box;
    font-size: 20px;
    font-weight: bold;
    color: #333;
    text-align: center;
    line-height: 200px;
    user-select: none; /* 禁止用户选择文本 */
  }
  .front  { transform: translateZ(100px); } /* 前面 */
  .back   { transform: rotateY(180deg) translateZ(100px); } /* 后面 */
  .right  { transform: rotateY(90deg) translateZ(100px); } /* 右面 */
  .left   { transform: rotateY(-90deg) translateZ(100px); } /* 左面 */
  .top    { transform: rotateX(90deg) translateZ(100px); } /* 上面 */
  .bottom { transform: rotateX(-90deg) translateZ(100px); } /* 下面 */
  
  @keyframes rotateCube {
    from { transform: rotateX(0deg) rotateY(0deg); } /* 动画开始时的状态 */
    to   { transform: rotateX(360deg) rotateY(360deg); } /* 动画结束时的状态 */
  }
</style>
</head>
<body>
<div class="scene">
  <div class="cube">
    <div class="face front">Front</div>
    <div class="face back">Back</div>
    <div class="face right">Right</div>
    <div class="face left">Left</div>
    <div class="face top">Top</div>
    <div class="face bottom">Bottom</div>
  </div>
</div>
</body>
</html>

编辑

2.3. 3D变换的浏览器兼容性

        大多数现代浏览器都支持CSS3的3D变换,但为了确保兼容性,最好检查并测试在不同浏览器上的表现。

解决方案

1.使用css前缀

        可以针对不同浏览器添加相应的CSS前缀(例如-moz-、-ms-、-webkit-等)。

2.使用JavaScript库

        可以使用一些JavaScript库(如Three.js、Babylon.js等)来实现跨浏览器的3D变换效果。

这些库封装了底层的浏览器兼容性处理,简化了开发过程,并提供了丰富的3D图形功能。

3.检测浏览器支持

        通过JavaScript代码检测浏览器是否支持CSS3的3D变换属性。

        如果浏览器不支持3D变换,则提供替代方案或降级处理,以确保用户体验的连续性。

4.考虑使用其他技术

        如果3D变换效果在特定浏览器中无法实现,开发者可以考虑使用其他技术来实现相似的效果。

        例如,可以使用SVG、Canvas等技术来绘制和渲染3D图形。

三、calc函数在动画中的应用

calc()函数允许开发者在CSS中执行一些计算,以动态地设置元素的属性。在动画中,calc()函数可以用于计算元素的位移、大小等属性,从而创建更复杂的动画效果。

1、基本用法

calc()函数支持四则运算和混合单位,如百分比、px、em等。

在使用calc()函数时,需要注意运算符两侧的空格要求。

2、在动画中的应用

可以使用calc()函数来计算元素的位移量,以实现更平滑的动画过渡效果。

例如,可以使用calc(50% - 50px)来设置一个元素相对于其父元素宽度的一半再减去50px的位置。

注意:- + * / 四种运算的运算符两边必须有空格,不然不执行

感恩家人们点点赞

❌
❌