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>

五、代码效果呈现

代码效果如下图所示:

相关推荐
kingwebo'sZone9 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_090110 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农10 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king10 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳10 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵11 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星11 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_12 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝12 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions12 小时前
2026年,微前端终于“死“了
前端·状态模式