Web前端制作一个评论发布案例

完成该案例需要用到的知识点有:

一、HTML基础

  1. 语义化标签使用: textarea 多行输入框、 ul/li 列表承载动态内容、 span 行内元素展示字数/操作按钮

  2. 表单属性: maxlength 限制输入框最大字符数、 placeholder 输入提示

  3. 元素标识: id 用于精准获取DOM元素, class 用于样式控制和类名判断

二、CSS基础&布局

  1. 通用样式重置: * {margin:0;padding:0;box-sizing:border-box;} 统一盒模型、清除默认边距

  2. 布局技巧: flex 布局实现头像+内容的横向排列、 float 实现字数统计与发布按钮的右对齐; flex-shrink:0 防止头像被挤压

  3. 样式控制: resize:none 禁止文本域拉伸、 cursor:pointer/not-allowed 鼠标样式交互、 disabled 状态的样式覆写

  4. 辅助样式: word-break:break-all 实现长文本自动换行、 dashed 虚线边框做列表分隔、 border-radius:50% 实现圆形头像

  5. 溢出处理: overflow:hidden 清除浮动带来的父元素高度塌陷问题

三、JavaScript 核心(重点)

  1. DOM 元素操作
  • 元素获取: document.getElementById() 根据ID获取指定DOM元素(核心获取方式)

  • 元素创建: document.createElement() 动态创建节点(如li)

  • 内容插入: innerHTML 为元素设置HTML结构(支持拼接标签字符串,高效创建复杂节点); insertBefore(新节点, 参考节点) 实现新内容前置插入(发布的内容显示在最顶部)

  • 元素删除: parentElement.remove() 通过子元素找到父元素并删除整个节点

  • 内容重置:通过修改元素 value (文本域)、 textContent (普通元素)实现内容清空/更新

  1. 事件处理
  • 事件绑定: addEventListener() 为元素绑定事件(推荐方式,可绑定多个同类型事件)

  • 核心事件类型: input 事件(文本域实时输入监听,区别于 keyup ,兼容粘贴/输入法等场景); click 点击事件(发布、删除操作)

  • 事件委托:将删除事件绑定在父元素(ul) 上,通过事件对象 e.target 判断触发源(是否为删除按钮 .the_del ),解决动态生成元素无法绑定事件的问题

  • 事件对象: e.target 获取事件的实际触发元素,通过 classList.contains() 判断元素是否包含指定类名

  1. 数据处理与交互
  • 文本处理: trim() 去除字符串首尾空格,避免发布空内容/纯空格内容

  • 状态控制:通过修改元素 disabled 属性,控制发布按钮的可用/禁用状态(无内容时禁用,有内容时启用)

  • 随机数应用: Math.floor(Math.random() * 数组长度) 实现从数组中随机选取元素(随机获取用户头像和昵称)

  • 日期时间格式化: new Date() 获取当前时间,通过 getFullYear()/getMonth()/getDate() 等方法获取时间分量; padStart(2, '0') 实现补零操作(如1月→01月,9分→09分),保证时间格式统一

  1. 数组与对象基础
  • 数组取值:通过索引获取数组中的对象元素(如 userList[索引] )

  • 对象属性访问:通过 . 访问对象的属性(如 randomUser.uname / randomUser.imgSrc )

  1. 页面初始化
  • 初始状态设置:页面加载时,主动设置发布按钮的 disabled = true ,避免初始状态按钮可点击的不合理情况
  1. 字符串拼接
  • 模板字符串(隐式):通过 ${变量} 拼接HTML结构和动态数据(昵称、头像、时间、发布内容),简化字符串拼接操作

四、代码的实现

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>微博发布</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    ul {
      list-style: none;
    }
    .w {
      width: 900px;
      margin: 0 auto;
    }
    .controls {
      overflow: hidden;
      margin-top: 20px;
    }
    .controls p {
      color: #409eff;
      margin-bottom: 8px;
      font-size: 16px;
    }
    .controls textarea {
      width: 100%;
      height: 120px;
      resize: none;
      border: 1px solid #e4e7ed;
      border-radius: 8px;
      outline: none;
      padding: 12px;
      font-size: 16px;
    }
    .controls div {
      float: right;
      margin-top: 10px;
    }
    .controls div span {
      color: #666;
      font-size: 14px;
    }
    .controls div .useCount {
      color: red;
    }
    .controls div button {
      width: 80px;
      outline: none;
      border: none;
      background: #0088ff;
      height: 32px;
      cursor: pointer;
      color: #fff;
      font-size: 14px;
      border-radius: 4px;
      margin-left: 10px;
    }
    .controls div button:hover {
      background: #0066cc;
    }
    .controls div button:disabled {
      background: #a0cfff;
      cursor: not-allowed;
    }
    .contentList {
      margin-top: 40px;
    }
    .contentList li {
      padding: 16px 0;
      border-bottom: 1px dashed #e4e7ed;
      position: relative;
      display: flex;
      gap: 12px;
    }
    .contentList li .userpic {
      width: 48px;
      height: 48px;
      border-radius: 50%;
      flex-shrink: 0;
    }
    .contentList li .content-wrap {
      flex: 1;
    }
    .contentList li .username {
      font-size: 14px;
      font-weight: 500;
      color: #303133;
      margin-bottom: 4px;
      display: block;
    }
    .contentList li .send-time {
      font-size: 12px;
      color: #909399;
      margin-bottom: 8px;
      display: block;
    }
    .contentList li .content {
      font-size: 14px;
      color: #606266;
      line-height: 1.5;
      word-break: break-all;
    }
    .contentList li .the_del {
      position: absolute;
      right: 0;
      top: 16px;
      font-size: 18px;
      cursor: pointer;
      color: #909399;
    }
  </style>
</head>
<body>
  <div class="w">
    <div class="controls">
      <p>有什么新鲜事想告诉大家?</p>
      <textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
      <div>
        <span class="useCount" id="useCount">0</span>
        <span>/</span>
        <span>200</span>
        <button id="send">发布</button>
      </div>
    </div>
    <div class="contentList">
      <ul id="list"></ul>
    </div>
  </div>
 
  <script>
    // 模拟用户数据
    const userList = [
      { uname: '司马懿', imgSrc: '../image/图1.png' },
      { uname: '女娲', imgSrc: '../image/图2.png'  },
      { uname: '百里守约', imgSrc: '../image/图3.png'  },
      { uname: '亚瑟', imgSrc:'../image/图4.png' },
      { uname: '虞姬', imgSrc: '../image/图5.png'  },
      { uname: '张良', imgSrc: '../image/图6.png'  },
      { uname: '安其拉', imgSrc: '../image/图7.png'  },
      { uname: '李白', imgSrc: '../image/图8.jpg'  },
      { uname: '阿珂', imgSrc: '../image/图9.jpg' },
      { uname: '墨子', imgSrc: '../image/图10.jpg' },
    ];

 
    // DOM元素
    const area = document.getElementById('area');
    const useCount = document.getElementById('useCount');
    const sendBtn = document.getElementById('send');
    const list = document.getElementById('list');
 
    // 1. 实时更新字数统计
    area.addEventListener('input', function () {
      const len = this.value.trim().length;
      useCount.textContent = len;
      // 控制发布按钮状态
      sendBtn.disabled = len === 0;
    });
 
    // 2. 发布评论
    sendBtn.addEventListener('click', function () {
      const content = area.value.trim();
      if (!content) return;
 
      // 随机选择一个用户作为评论者
      const randomUser = userList[Math.floor(Math.random() * userList.length)];
      // 获取当前时间
      const now = new Date();
      const timeStr = `${now.getFullYear()}年${(now.getMonth() + 1).toString().padStart(2, '0')}月${now.getDate().toString().padStart(2, '0')}日 ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
 
      // 创建评论元素
      const li = document.createElement('li');
      li.innerHTML = `
        <img class="userpic" src="${randomUser.imgSrc}" alt="${randomUser.uname}">
        <div class="content-wrap">
          <span class="username">${randomUser.uname}</span>
          <span class="send-time">发布于 ${timeStr}</span>
          <div class="content">${content}</div>
        </div>
        <span class="the_del">×</span>
      `;
 
      // 添加到列表最前面
      list.insertBefore(li, list.firstElementChild);
 
      // 清空输入框并重置字数
      area.value = '';
      useCount.textContent = '0';
      sendBtn.disabled = true;
    });
 
    // 3. 删除评论
    list.addEventListener('click', function (e) {
      if (e.target.classList.contains('the_del')) {
        e.target.parentElement.remove();
      }
    });
 
    // 初始化按钮状态
    sendBtn.disabled = true;
  </script>
</body>
</html>

五、代码效果呈现

代码效果如下图所示:

相关推荐
摘星编程2 小时前
React Native + OpenHarmony:useId唯一标识生成
javascript·react native·react.js
2603_949462102 小时前
Flutter for OpenHarmony社团管理App实战:消息中心实现
android·javascript·flutter
秋秋小事2 小时前
可选链与非空操作符
前端
iRuriCatt2 小时前
智慧景区管理系统 | 计算机毕设项目
java·前端·spring boot·vue·毕设
程序员清洒3 小时前
Flutter for OpenHarmony:Icon 与 IconButton — 图标系统集成
前端·学习·flutter·华为
萧曵 丶3 小时前
JavaScript 函数各种写法和场景
开发语言·javascript·ecmascript
西红市杰出青年3 小时前
CSS 选择器详细教程:原理、语法、方向/“轴”与实战
css·python
Yolanda944 小时前
【项目经验】钉钉免密登录实现
前端·javascript·钉钉
2601_949613024 小时前
flutter_for_openharmony家庭药箱管理app实战+药品详情实现
java·前端·flutter