HTML 基础 · 42/88
1. 微信小程序 bindtap 和 catchtap 的区别 2. Bootstrap 徽章 3. Bootstrap 按钮下拉菜单 4. Bootstrap 按钮组 5. Bootstrap 按钮激活与禁用 6. Bootstrap 文档类型声明 7. Bootstrap 下拉菜单 8. Bootstrap 表单帮助文本 9. Bootstrap 水平表单 10. Bootstrap 输入框组 11. Bootstrap 超大屏幕 12. Bootstrap 标签 13. Bootstrap 导航类型 14. Bootstrap 分页 15. Bootstrap 响应式表格 16. Bootstrap 垂直表单创建 17. 浏览器乱码问题及解决方案 18. Canvas 标签属性与 CSS 样式设置宽高的区别 19. Canvas、SVG、WebGL 对比 20. 网页验证码的作用 21. 前端跨页面通信方法 22. 圆形可点击区域实现 23. 浏览器多标签页通信方案 24. CSSOM 树和 DOM 树的解析时机 25. 设备的 DPR 是否可变 26. 禁用 a 标签的跳转和定位 27. DOM 和 BOM 的区别 28. DOM 发展历程 29. DOCTYPE 与文档模式 30. DNS 预解析优化网页加载速度 31. DOM 树的理解 32. Drag API 拖拽事件 33. 前端 SEO 优化要点 34. 标题与副标题的实现 35. HTML 元素分类 36. HTML 全局属性 37. HTML 页面渲染过程 38. HTML 语义化 39. HTML 语义化 40. HTML5 DOCTYPE 声明简化原因 41. HTML5 离线存储原理与使用 42. HTML5 新特性 43. HTML5 移除的元素 44. IconFont 字体图标 45. iframe 的优缺点与通信 46. 图片点击下载而非预览 47. HTML 和 CSS 中的图片加载与渲染规则 48. 浏览器预览待上传图片 49. img 标签 title 和 alt 的区别 50. input 标签触发拍照功能 51. 控制 input 输入框字数 52. img 的 srcset 属性 53. 禁止 input 显示历史记录 54. input 上传多个文件 55. JS 和 CSS 对 DOM 树构建的影响 56. label 标签的作用 57. link 和 @import 的区别 58. Meta 标签常用属性 59. Meta 标签自动刷新跳转 60. 小程序的双线程架构 61. 小程序页面间传递数据的方法 62. 小程序为什么没有 DOM API 63. 微信小程序的优劣势 64. Node 和 Element 的关系 65. 页面生命周期事件:DOMContentLoaded、load、beforeunload、unload 66. 渐进增强与优雅降级 67. 渐进式 JPEG 图片格式 68. PV 和 UV 的区别 69. Script 标签 defer 和 async 70. 实现点击回到顶部功能 71. script 标签能否使用自闭合语法 72. src 与 href 的区别 73. SSG 静态网站生成 74. style 标签位置对页面渲染的影响 75. 从输入 URL 到页面显示的过程 76. Web 标准与可访问性理解 77. 网页常用图片格式 78. 网页常用图片格式 79. Web 标准与 W3C 标准 80. WebSocket 低版本浏览器兼容方案 81. Web Worker 的作用与场景 82. 微信小程序事件传值 83. 微信小程序文件结构 84. 微信小程序的架构 85. 微信小程序原理 86. 页面白屏时间优化 87. 小程序 WXSS 与 CSS 的区别 88. XHTML 与 HTML 的区别

HTML5 新特性

HTML5 常用新特性及使用示例

问题

列举 HTML5 的主要新特性,包括语义化标签、Video/Audio、Canvas、LocalStorage、Drag API、Web Worker。

解答

1. 语义化标签

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>语义化标签示例</title>
</head>
<body>
  <!-- 页面头部 -->
  <header>
    <nav>
      <ul>
        <li><a href="/">首页</a></li>
        <li><a href="/about">关于</a></li>
      </ul>
    </nav>
  </header>

  <!-- 主要内容 -->
  <main>
    <article>
      <header>
        <h1>文章标题</h1>
        <time datetime="2024-01-01">2024年1月1日</time>
      </header>
      <section>
        <p>文章内容...</p>
      </section>
      <footer>
        <p>作者:张三</p>
      </footer>
    </article>

    <!-- 侧边栏 -->
    <aside>
      <h2>相关推荐</h2>
    </aside>
  </main>

  <!-- 页面底部 -->
  <footer>
    <p>&copy; 2024</p>
  </footer>
</body>
</html>

常用语义化标签:

标签用途
<header>页面或区块的头部
<nav>导航链接
<main>页面主要内容
<article>独立的文章内容
<section>文档中的节
<aside>侧边栏内容
<footer>页面或区块的底部
<figure>图片、图表等
<figcaption>figure 的标题
<time>日期时间
<mark>高亮文本

2. Video/Audio

<!-- 视频播放 -->
<video 
  id="myVideo" 
  width="640" 
  height="360" 
  controls 
  poster="cover.jpg"
>
  <source src="video.mp4" type="video/mp4">
  <source src="video.webm" type="video/webm">
  您的浏览器不支持视频播放
</video>

<!-- 音频播放 -->
<audio id="myAudio" controls>
  <source src="audio.mp3" type="audio/mpeg">
  <source src="audio.ogg" type="audio/ogg">
  您的浏览器不支持音频播放
</audio>

<script>
const video = document.getElementById('myVideo')

// 播放控制
video.play()
video.pause()

// 常用属性
console.log(video.currentTime) // 当前播放时间
console.log(video.duration)    // 总时长
console.log(video.paused)      // 是否暂停
console.log(video.volume)      // 音量 0-1

// 常用事件
video.addEventListener('play', () => console.log('开始播放'))
video.addEventListener('pause', () => console.log('暂停'))
video.addEventListener('ended', () => console.log('播放结束'))
video.addEventListener('timeupdate', () => {
  console.log('播放进度:', video.currentTime)
})
</script>

3. Canvas

<canvas id="myCanvas" width="400" height="300"></canvas>

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

// 绘制矩形
ctx.fillStyle = '#3498db'
ctx.fillRect(10, 10, 100, 80)

// 绘制边框矩形
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 3
ctx.strokeRect(130, 10, 100, 80)

// 绘制圆形
ctx.beginPath()
ctx.arc(300, 50, 40, 0, Math.PI * 2)
ctx.fillStyle = '#2ecc71'
ctx.fill()

// 绘制线条
ctx.beginPath()
ctx.moveTo(10, 120)
ctx.lineTo(100, 180)
ctx.lineTo(200, 120)
ctx.strokeStyle = '#9b59b6'
ctx.lineWidth = 2
ctx.stroke()

// 绘制文字
ctx.font = '24px Arial'
ctx.fillStyle = '#333'
ctx.fillText('Hello Canvas', 10, 230)

// 绘制图片
const img = new Image()
img.onload = () => {
  ctx.drawImage(img, 200, 150, 100, 100)
}
img.src = 'image.png'

// 导出为图片
const dataURL = canvas.toDataURL('image/png')
</script>

4. LocalStorage / SessionStorage

// ========== LocalStorage ==========
// 数据持久存储,关闭浏览器后仍然保留

// 存储数据
localStorage.setItem('username', '张三')
localStorage.setItem('user', JSON.stringify({ id: 1, name: '张三' }))

// 读取数据
const username = localStorage.getItem('username')
const user = JSON.parse(localStorage.getItem('user'))

// 删除数据
localStorage.removeItem('username')

// 清空所有数据
localStorage.clear()

// 获取存储数量
console.log(localStorage.length)

// 遍历所有数据
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i)
  console.log(key, localStorage.getItem(key))
}

// ========== SessionStorage ==========
// 会话存储,关闭标签页后清除,API 与 localStorage 相同

sessionStorage.setItem('token', 'abc123')
const token = sessionStorage.getItem('token')

// ========== 监听存储变化 ==========
// 在其他标签页修改 storage 时触发
window.addEventListener('storage', (e) => {
  console.log('key:', e.key)
  console.log('旧值:', e.oldValue)
  console.log('新值:', e.newValue)
  console.log('来源:', e.url)
})

// ========== 封装工具函数 ==========
const storage = {
  set(key, value, expires) {
    const data = {
      value,
      expires: expires ? Date.now() + expires : null
    }
    localStorage.setItem(key, JSON.stringify(data))
  },
  
  get(key) {
    const item = localStorage.getItem(key)
    if (!item) return null
    
    const data = JSON.parse(item)
    // 检查是否过期
    if (data.expires && Date.now() > data.expires) {
      localStorage.removeItem(key)
      return null
    }
    return data.value
  },
  
  remove(key) {
    localStorage.removeItem(key)
  }
}

// 使用:存储 1 小时过期的数据
storage.set('cache', { data: 'test' }, 60 * 60 * 1000)

5. Drag and Drop

<style>
  .drag-item {
    width: 100px;
    height: 100px;
    background: #3498db;
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: move;
    margin: 10px;
  }
  .drop-zone {
    width: 300px;
    height: 200px;
    border: 2px dashed #ccc;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .drop-zone.over {
    border-color: #3498db;
    background: #ecf0f1;
  }
</style>

<!-- 可拖拽元素 -->
<div 
  class="drag-item" 
  draggable="true" 
  id="item1"
>
  拖拽我
</div>

<!-- 放置区域 -->
<div class="drop-zone" id="dropZone">
  放置区域
</div>

<script>
const dragItem = document.getElementById('item1')
const dropZone = document.getElementById('dropZone')

// 拖拽开始
dragItem.addEventListener('dragstart', (e) => {
  // 设置拖拽数据
  e.dataTransfer.setData('text/plain', e.target.id)
  e.dataTransfer.effectAllowed = 'move'
  // 添加拖拽样式
  e.target.style.opacity = '0.5'
})

// 拖拽结束
dragItem.addEventListener('dragend', (e) => {
  e.target.style.opacity = '1'
})

// 拖拽进入放置区域
dropZone.addEventListener('dragenter', (e) => {
  e.preventDefault()
  dropZone.classList.add('over')
})

// 在放置区域上方移动
dropZone.addEventListener('dragover', (e) => {
  e.preventDefault() // 必须阻止默认行为才能触发 drop
  e.dataTransfer.dropEffect = 'move'
})

// 离开放置区域
dropZone.addEventListener('dragleave', (e) => {
  dropZone.classList.remove('over')
})

// 放置
dropZone.addEventListener('drop', (e) => {
  e.preventDefault()
  dropZone.classList.remove('over')
  
  // 获取拖拽数据
  const id = e.dataTransfer.getData('text/plain')
  const element = document.getElementById(id)
  
  // 移动元素到放置区域
  dropZone.appendChild(element)
})
</script>

6. Web Worker

// ========== 主线程 main.js ==========
// 创建 Worker
const worker = new Worker('worker.js')

// 发送消息给 Worker
worker.postMessage({
  type: 'calculate',
  data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
})

// 接收 Worker 返回的消息
worker.onmessage = (e) => {
  console.log('计算结果:', e.data)
}

// 错误处理
worker.onerror = (e) => {
  console.error('Worker 错误:', e.message)
}

// 终止 Worker
// worker.terminate()

// ========== Worker 线程 worker.js ==========
// 接收主线程消息
self.onmessage = (e) => {
  const { type, data } = e.data
  
  if (type === 'calculate') {
    // 执行耗时计算(不会阻塞主线程)
    const result = heavyCalculation(data)
    
    // 返回结果给主线程
    self.postMessage(result)
  }
}

function heavyCalculation(arr) {
  // 模拟耗时操作
  let sum = 0
  for (let i = 0; i < 1000000000; i++) {
    sum += i
  }
  return arr.reduce((a, b) => a + b, 0)
}

// Worker 内部关闭自己
// self.close()

// ========== 内联 Worker ==========
// 不需要单独的 js 文件
const workerCode = `
  self.onmessage = (e) => {
    const result = e.data * 2
    self.postMessage(result)
  }
`
const blob = new Blob([workerCode], { type: 'application/javascript' })
const inlineWorker = new Worker(URL.createObjectURL(blob))

inlineWorker.postMessage(10)
inlineWorker.onmessage = (e) => {
  console.log('内联 Worker 结果:', e.data) // 20
}

7. 其他新特性

// ========== Geolocation 地理定位 ==========
navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log('纬度:', position.coords.latitude)
    console.log('经度:', position.coords.longitude)
  },
  (error) => {
    console.error('定位失败:', error.message)
  }
)

// ========== History API ==========
// 添加历史记录
history.pushState({ page: 1 }, '标题', '/page1')

// 替换当前记录
history.replaceState({ page: 2 }, '标题', '/page2')

// 监听前进后退
window.addEventListener('popstate', (e) => {
  console.log('状态:', e.state)
})

// ========== 新的表单特性 ==========
/*
<input type="email" required>
<input type="url">
<input type="number" min="0" max="100">
<input type="range" min="0" max="100">
<input type="date">
<input type="color">
<input type="search" placeholder="搜索...">
<input pattern="[0-9]{11}" title="请输入11位手机号">
*/

// ========== 新的选择器 API ==========
document.querySelector('.class')
document.querySelectorAll('div')
element.classList.add('active')
element.classList.remove('active')
element.classList.toggle('active')
element.classList.contains('active')

关键点

  • 语义化标签:提升可读性和 SEO,常用 header/nav/main/article/section/aside/footer
  • Video/Audio:原生多媒体支持,通过 source 提供多格式兼容
  • Canvas:2D 绘图 API,适合图表、游戏、图像处理
  • LocalStorage:持久存储 5MB,SessionStorage 会话级存储,都是同步 API
  • Drag API:原生拖拽支持,核心是 dragstart/dragover/drop 事件
  • Web Worker:独立线程执行耗时任务,通过 postMessage 通信,不能操作 DOM