JavaScript编程基础(六)表单事件、模块的使用与实战之留言板、经典选项卡

原创 阁主  2026-03-18 16:53:47  阅读 2629 次 评论 0 条
摘要:

继续上次的JavaScript编程基础(五)继续学习,简单记录学习PHP中文网23期JavaScript基础知识,内容包括:表单事件、模块的使用、实战留言板、实战经典选项卡

表单事件

  • 常用事件
  1. submit: 提交
  2. focus: 焦点
  3. blur: 失去焦点
  4. change: 值改变,且失去焦点时
  5. input: 值一旦改变就触发, 不等失去焦点
  • 禁用表单默认提交行为的 3 种方法
  1. form.onsubmit = 'return false'
  2. form.button.type = 'button'
  3. event.preventDefault()

form.html:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>表单事件</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <form action="check.php" id="login" method="post">
      <fieldset>
        <legend class="title">请登录</legend>
        <div>
          <input type="email" name="email" value="admin@php.cn" autofocus />
          <input type="password" name="password" value="123456" />
          <button>登录</button>
        </div>
      </fieldset>
    </form>
    <script>
      //   const login = document.querySelector('#login')
      const login = document.forms.login;

      // 1.提交事件: submit, onsubmit
      login.onsubmit = function () {
        alert("提交成功");
        // 通常在提交之前要做数据验证, 再提交,一般会禁用"默认提交行为"
        // 有二种方式
        // 方式1: 返回 false
        // return false
        // 方式2: 用事件对象的方法来禁用默认行为
        // event.preventDefault()
      };

      // 除了操作表单元素<form>的submit之外,还可以修改<button>的type类型来实现禁用行为
      const btn = document.querySelector("#login button");
      btn.type = "button";
      //   修改了button.type属性,将submit -> button , 这个按钮就只是视觉效果,没有提交行为

      /**
       * submit事件
       * 默认提交时自动触发,禁用方式有二个
       * 1. 事件方法: 返回false或禁用默认行为ev.preventDefault()
       * 2. 修改提交按钮类型: submit -> button
       */

      // =========================================

      /**
       * 其它表单事件
       * focus: 获取焦点时
       * blur: 失去焦点
       * change: 值改变,且失去焦点时
       * input: 值改变即触发,不等失去焦点
       * select: 选择文本时触发,用适用于<input type=text>或<textarea>
       */

      // change
      login.email.onchange = function () {
        console.log(this.value);
      };

      // input
      login.email.oninput = function () {
        console.log(this.value);
      };

      // blur
      login.password.onblur = function () {
        if (this.value.length < 6) console.warn("密码不能少于6位");
      };
    </script>

    <!-- 禁用a标签的跳转行为 -->
    <a href="https://www.php.cn" id="jump">php.cn</a>
    <script>
      document.querySelector("#jump").onclick = function () {
        event.preventDefault();
      };
    </script>
  </body>
</html>

style.css:

#login {
  width: 20em;
  margin: 30px auto;
}
#login fieldset {
  padding: 1em 2em;
  border: none;
  border-radius: 0.5em;
  box-shadow: 0 0 3px #666;
  background: linear-gradient(to left top, lightcyan, white);
}
#login fieldset:hover {
  box-shadow: 0 0 8px #666;
  transition: 0.3s;
  cursor: pointer;
}
#login fieldset .title {
  text-align: center;
  padding: 0 0.6em;
  font-size: 1.3em;
  background-color: #fff;
}
#login fieldset div {
  display: grid;
  gap: 1em;
}
#login input {
  height: 2em;
  border: none;
  background: transparent;
  border-bottom: 1px solid #aaa;
  outline: none;
  outline: none;
}
#login button {
  height: 3em;
  border: none;
  cursor: pointer;
  background-color: seagreen;
  border-radius: 0.5em;
  color: white;
}
#login button:hover {
  opacity: 0.8;
  transition: 0.3s;
}

实战留言板

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>留言板(toDoList)</title>
  </head>
  <body>
    <!-- 输入框: 用户按下回车键就提交留言 -->
    <input
      type="text"
      placeholder="请留言"
      onkeydown="addMessage(this)"
      autofocus
    />
    <!-- 留言区 -->
    <ul class="list"></ul>

    <script>
      function addMessage(ele) {
        // 1. 判断用户是否按下了回车?错误优先的原则
        if (event.key !== "Enter") return false;

        // 2. 判断用户的留言是否为空?
        if (ele.value.length === 0) {
          alert("留言不能为空");
          return false;
        }

        // 3. 添加留言
        const list = document.querySelector(".list");
        // 创建一条留言
        let htmlStr = `
            <li>
                <span>${ele.value}</span>
                <!-- this.parentNode 返回父节点的li -->
                <button onclick="del(this.parentNode)">删除</button>
            </li>
        `;
        // 最新留言应该永远显示在第一条,在<ul>的第一个子元素的位置上
        list.insertAdjacentHTML("afterbegin", htmlStr);

        // 4. 清空留言
        ele.value = "";
      }

      // 删除
      function del(ele) {
        if (confirm("是否删除?")) ele.remove();

        // outerHTML: 当前的元素的html字符串描述
        // if (confirm('是否删除?')) ele.outerHTML = null
      }
    </script>
  </body>
</html>

模块的使用

  • 模块成员导出
/**
 * 模块的知识
 * 1. 一个js文件,就是一个模块
 * 2. 一个js文件是不是模块,由调用环境决定
 * 3. 全局,函数,块,现在又多了一个作用域: 模块作用域
 * 4. 模块内的成员,默认全是私有的(局部的),外部不可见(类似函数内的成员)
 * 5. export : 模块成员导出
 * 6. import: 模块成员的导入
 */

// 模块成员
let uname = '安欣'
let greet = function (uname) {
  return 'Hello, ' + uname
}
// 1. 逐个导出
export let uname = '安欣'
export let greet = function (uname) {
  return 'Hello, ' + uname
}
// 2. 批量导出

// 声明部分
let username = '高启强'
let greeting = function (uname) {
  return 'Hello, ' + uname
}
// 导出部分
export { uname, greet }
// 2. 批量别名导出

// 声明部分
// let username = '高启强'
// let greeting = function (uname) {
//   return 'Hello, ' + uname
// }
let uname = '高启强'
let greet = function (uname) {
  return 'Hello, ' + uname
}
// 导出部分
// export { uname, greet }
// as 别名导出
export { username as uname, greeting as greet }
// 3. 打包导出
// 适用于一个对象或数组的导出
export class User {
  constructor(uname) {
    this.uname = uname
  }
  greet() {
    return '欢迎您, ' + this.uname
  }
}
// 4. 默认导出(匿名导出)
export default class {
  constructor(uname) {
    this.uname = uname
  }
  greet() {
    return '欢迎您, ' + this.uname
  }
}
  • 模块使用
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模块的使用</title>
  </head>
  <body>
    <!-- 如果想使用模块,必须设置script.type = "module" -->
    <script type="module">
      // import 匿名对象 from 模块的路径
      // {} : 匿名对象
      //   let uname
      //   import { uname as username, greet } from './module.js'
      //   console.log(username)
      //   console.log(greet(username))

      //   import { User } from './module.js'

      // 默认导出, 能不能 {} 接收?
      // 不行, 因为匿名对象中的属性必须要有名字
      // 此时, 必须使用一个命名常量来接收
      //   import User from './module.js'
      import Demo from "./module.js";

      //   console.log(User)
      const user = new Demo("龙哥");
      console.log(user.greet());
    </script>
  </body>
</html>

实战经典选项卡

index.html:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>模块实战: 选项卡</title>
  </head>
  <style>
    /* 隐藏类 */
    .hidden {
      display: none;
    }
    /* 显示类 */
    .active {
      display: block;
    }

    /* 激活类 */
    .type > *.active,
    .content > *.active {
      background-color: lightblue;
    }
  </style>
  <body>
    <div class="box">
      <!-- 1. 栏目组 -->
      <div class="type" style="display: flex"></div>

      <!-- 2. 内容组 -->
      <div class="content"></div>
    </div>

    <script type="module">
      //   import { cates, details, createTab, setBtnStatus, setContentStatus } from './tabs.js'
      // tabs: 就是一个对象,把导入的成员做为它的属性,全部挂载到它的上面
      // tabs: 相当于"命名空间"
      import * as tabs from "./tabs.js";
      console.log(tabs);
      console.log(tabs.cates);
      console.log(tabs.details);
      //   console.log(createTab)

      // 获取栏目元素
      const type = document.querySelector(".type");
      // 获取新闻列表元素
      const content = document.querySelector(".content");

      // 页面加载时就把内容渲染出来
      window.onload = tabs.createTab(type, content);

      // 利用"事件代理",将每个按钮的点击,全部委托给父级div.type来实现
      type.onclick = (ev) => {
        // 1. 设置栏目高亮
        tabs.setBtnStatus(ev);
        // 2. 激活与当前栏目对应的新闻列表
        tabs.setContentStatus(ev, ev.target);
      };
    </script>
  </body>
</html>

tabs.js:

// 选项卡模块
// 1. 栏目
const cates = [
  { cid: 1, cname: "中国新闻" },
  { cid: 2, cname: "国际新闻" },
  { cid: 3, cname: "安徽新闻" },
];

// 2. 内容
// 内容的key 必须与 栏目的id绑定
const details = [
  {
    key: 1,
    cid: 1,
    content: [
      {
        title: "蔡英文窜美,美官员警告中国不要过度反应,外交部回应",
        url: "https://news.ifeng.com/c/8OYFjT9c3KM",
      },
      {
        title: "马英九:从来没有一次像今天受到这么大的冲击",
        url: "https://news.ifeng.com/c/8OYI5PZyyRs",
      },
      {
        title: "美媒:王毅与沙利文进行电话交谈",
        url: "https://news.ifeng.com/c/8OXw5JWvnRM",
      },
    ],
  },
  {
    key: 2,
    cid: 2,
    content: [
      {
        title: "美核航母抵韩,半岛局势会失控吗?",
        url: "https://news.ifeng.com/c/8OYApVnV1mN",
      },
      {
        title:
          "西班牙法律允许强占住房:有华人3年没回西班牙,房子被吉普赛人占了",
        url: "https://news.ifeng.com/c/8OXfZLPso8P",
      },
      {
        title: "拜登首次就普京拟在白俄部署核武器表态",
        url: "https://news.ifeng.com/c/8OXdmTTNQD6",
      },
    ],
  },
  {
    key: 3,
    cid: 3,
    content: [
      {
        title: "省级党政代表团密集赴皖考察!安徽究竟有何看点?",
        url: "https://ah.ifeng.com/c/8OXtD8eq0pA",
      },
      {
        title: "合肥、蚌埠、亳州、安庆、宣城最新人事任免!",
        url: "https://ah.ifeng.com/c/8OXnxW9z3K5",
      },
      {
        title: "下月起合肥坐高铁到香港!最快只需7时26分",
        url: "https://ah.ifeng.com/c/8OXheuq5n55",
      },
    ],
  },
];

// 3. 创建栏目和内容
function createTab(type, content) {
  // 1. 生成栏目
  for (let i = 0; i < cates.length; i++) {
    const btn = document.createElement("button");
    btn.textContent = cates[i].cname;

    // 第一个按钮应该是高亮显示
    // 为每个按钮添加一个自定义属性: data-index
    // data-key = cates[i].cid
    btn.dataset.key = cates[i].cid;
    if (i === 0) {
      btn.classList.add("active");
    }

    type.append(btn);
  }
  // 2. 生成内容
  for (let i = 0; i < details.length; i++) {
    // 创建<ul>
    const ul = document.createElement("ul");

    // 为每个<ul>添加自定义属性data-key
    ul.dataset.key = details[i].cid;

    // 全部内容加载时,默认全隐藏,只要显示第一组新闻列表即可
    ul.classList.add(i === 0 ? "active" : "hidden");

    // 循环: 将与列表对应的数据全部渲染出来
    for (let j = 0; j < details[i].content.length; j++) {
      // <li><a href="....">xxxxx</a></li>
      const li = document.createElement("li");
      const a = document.createElement("a");
      a.textContent = details[i].content[j].title;
      a.href = details[i].content[j].url;
      li.append(a);
      ul.append(li);
      content.append(ul);
    }
  }
}

// 4. 自动设置栏目高亮
function setBtnStatus(ev) {
  // 1. 去掉所有按钮的激活样式
  // console.log(...ev.currentTarget.children);
  [...ev.currentTarget.children].forEach((btn) =>
    btn.classList.remove("active")
  );

  // 2. 将当前用户正在点击的按钮添加active
  ev.target.classList.add("active");
}

// 5. 设置与栏目对应的内容的激活状态
/**
 * 参数
 * 1. 事件对象,event
 * 2. 当前正在被点击的按钮(选项)
 */
function setContentStatus(ev, currentBtn) {
  // 所有的新闻列表<ul>
  const lists = document.querySelectorAll(".content > ul");
  console.log(lists);
  // 1. 将激活的列表全部隐藏 active -> hidden
  lists.forEach((list) => list.classList.replace("active", "hidden"));

  // 2. 找到与栏目ID相同(对应的)新闻列表<ul>
  const currList = [...lists].find(
    (list) => list.dataset.key === currentBtn.dataset.key
  );
  console.log(currList);

  // 3. 设置当前列表为激活active
  currList.classList.replace("hidden", "active");
}
export { cates, details, createTab, setBtnStatus, setContentStatus };
本文地址:https://www.mainblog.cn/331.html
版权声明:本文为原创文章,版权归 阁主 所有,欢迎分享本文,转载请保留出处!
免责申明:有些内容源于网络,没能联系到作者。如侵犯到你的权益请告知,我们会尽快删除相关内容。
NEXT:已经是最新一篇了

评论已关闭!