前端笔记 - Form Input Wave

效果

formInputWave

代码地址

Live Demo

实现思路以及用到的东西

本身是 flex 布局的一个小表单项目,记录一下某些细节的实现方式。

Input 的样式

/* 点击 input 或焦点在 input 上时,去掉描边并更改颜色,用两个伪类实现 */

.form-container input:focus,
.form-container input:valid {
    outline: 0;
    border-bottom: 2px lightblue solid;
}

.form-container input {
    display: block;
    background-color: transparent;
    width: 100%;
    border: 0;
    border-bottom: 2px #fff solid;
    padding: 15px 0;
    font-size: 18px;
    color: #fff;
}

:focus

表示获得焦点的元素(如表单输入)。当用户点击或轻触一个元素或使用键盘的 Tab 键选择它时,它会被触发。

https://developer.mozilla.org/zh-CN/docs/Web/CSS/:focus

:valid

表示内容验证正确的<input> 或其他 <form> 元素。这能简单地将校验字段展示为一种能让用户辨别出其输入数据的正确性的样式。

Label 文字波动上移

部分 HTML 代码:

<form>
    <div class="form-container">
        <input type="text" required>
        <label>Email</label>
    </div>
    <div class="form-container">
        <input type="password" required>
        <label>Password</label>
    </div>

    <button class="btn">Login</button>
    <p class="text">
        Don't have an account? 
        <a href="#">Register</a>
    </p>
</form>

部分 CSS 代码:

.form-container label {
    position: absolute;
    top: 15px;
    pointer-events: none;
    /* 加了上面这行,鼠标才能穿透label点击input */
}

.form-container label span {
    display: inline-block;
    font-size: 18px;
    min-width: 5px;
    transition: 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
/* 对 span 设置了 inline-block 后,下面的 transform 才生效 */

.form-container input:focus ~ label span,
.form-container input:valid ~ label span {
    transform: translateY(-30px);
    color: lightblue;
}

JavaScript 代码:

const labels = document.querySelectorAll('.form-container label')

labels.forEach(label => {
    label.innerHTML = label.innerText
        .split('')
        .map( (letter,idx) => `<span style="transition-delay:${idx * 30}ms">${letter}</span>`)
        .join('')
})

这段代码为表单 label 标签中的每个字母创建独立动画效果:

将标签文本拆分为单个字符,每个字符包裹在带有递增过渡延迟的<span>中,最终配合 CSS 形成波浪式动画效果。

JavaScript 笔记

  1. DOM选择
document.querySelectorAll('.form-container label')
  1. 遍历元素
labels.forEach(...)
  1. 文本处理
label.innerText.split('')
  • 特性String.prototype.split
  • 作用:字符串转字符数组
  • 文档String.split - MDN
  • 引号内是用于分割的 separator ,省略 separator (即:‘’)或者传递 undefined 会返回只包含所调用字符串的数组,比如这个项目的效果是 [“E”, “m”, “a”, “i”, “l”]。
  1. 数组转换
.map((letter,idx) => `...`)
// 正确写法(保留前两个参数占位)
.map((element, index, originalArray) => {
    console.log(originalArray) // 原始数组的引用
    return element * originalArray.length
})

// 或
// 只使用元素
[1,2,3].map(n => n*2) 

// 使用元素和索引
[1,2,3].map((n, i) => n + i)

// 使用全部三个参数
[1,2,3].map((n, i, arr) => n + arr.length)

// 如果不需要某些参数
.map((_, _, arr) => arr.join('')) // 用下划线占位

Array.prototype.map 里的三个参数不能换位置,后面两个参数可选,但是如果用数组参数,就必须要留 index 的位置,用 _ 占位。

箭头函数用 {} 就必须有 return
只有一个变量时可以省略 ()
只有一条表达式时可以省略 {} ,且不要 return

  1. 模板字符串
`<span>${letter}</span>`
  • 特性模板字面量

  • 作用:动态字符串构建

  • 文档模板字符串 - MDN

    Q:${} 中可以写哪些内容?
    A:任何合法 JavaScript 表达式:

    `计算:${2 + 3 * 4}` // "计算:14"
    `时间:${new Date().toLocaleTimeString()}`
    `嵌套:${`子模板:${value}`}`

    Q:如何输出反引号?
    A:使用转义符:

    `这是一个\`反引号\`` // "这是一个`反引号`"

    Q:模板字符串会执行HTML转义吗?
    A:不会!需注意XSS防护:

    // 危险示例
    const userInput = '<script>alert(1)</script>';
    document.body.innerHTML = `${userInput}`; // 会执行脚本
    
    // 正确做法:使用textContent替代innerHTML
    // 危险!用户输入可能包含恶意脚本
    element.innerHTML = `<div>${userInput}</div>`;
    
    // 正确做法:转义特殊字符
    const safeHTML = `<div>${escapeHTML(userInput)}</div>`;
    
    function escapeHTML(str) {
        return str.replace(/&/g, '&')
                 .replace(/</g, '<')
                 .replace(/>/g, '>');
    }
  1. 数组合并
.join('')
  • 特性Array.prototype.join
  • 作用:数组转字符串
  • 文档Array.join - MDN
[1,2,3].join('-') // "1-2-3"
[1,2,3].join('💡') // "1💡2💡3"
  1. DOM更新
label.innerHTML = ...

innerHTML 包含 HTML 标签,而 innerText 剔除 HTML 标签,只保留文本,表示一个节点及其后代所渲染文本的内容。

归档 友链
theme