You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS自定义输入框:如何将邮箱标签嵌入输入框内

解决方案

要实现邮箱标签嵌入输入框内部的效果,我们需要调整DOM结构并配合CSS样式模拟输入框视觉效果,具体修改如下:

1. 修改React组件的DOM结构

将邮箱标签和输入框放入同一个容器内,同时修复原代码中的语法与事件问题,调整后的完整代码:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class App extends React.Component {
  // 创建输入框ref(用于后续可能的DOM操作,可选)
  inputRef = React.createRef();

  state = {
    items: [],
    value: "",
    error: null,
    editvalue:""
  };

  handleKeyDown = evt => {
    // 修复原数组语法错误:移除多余空引号
    if (["Enter", "Tab", " "].includes(evt.key)) {
      evt.preventDefault();

      const value = this.state.value.trim();

      if (value && this.isValid(value)) {
        this.setState({
          items: [...this.state.items, value],
          value: ""
        });
      }
    }
  };

  handleChange = evt => {
    this.setState({
      value: evt.target.value,
      error: null
    });
  };

  handleDelete = item => {
    this.setState({
      items: this.state.items.filter(i => i !== item)
    });
  };
  
  handleItemEdit = item =>{
    const result = this.state.items.filter(values=>values!==item)
    this.setState({
      value: item,
      error: null,
      items: result
    });
  }

  handlePaste = evt => {
    evt.preventDefault();

    const paste = evt.clipboardData.getData("text");
    const emails = paste.match(/[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/g);

    if (emails) {
      const toBeAdded = emails.filter(email => !this.isInList(email));
      this.setState({
        items: [...this.state.items, ...toBeAdded]
      });
    }
  };

  isValid(email) {
    let error = null;

    if (this.isInList(email)) {
      error = `${email} has already been added.`;
    }

    if (!this.isEmail(email)) {
      error = `${email} is not a valid email address.`;
    }

    if (error) {
      this.setState({ error });
      return false;
    }

    return true;
  }

  isInList(email) {
    return this.state.items.includes(email);
  }

  isEmail(email) {
    return /[\w\d\.-]+@[\w\d\.-]+\.[\w\d\.-]+/.test(email);
  }

  render() {
    return (
      <>
        {/* 新增外层容器,模拟输入框外观 */}
        <div className="input-container">
          {this.state.items.map(item => (
            <div className="tag-item" key={item} onClick={() => this.handleItemEdit(item)}>
              {item}
              <button
                type="button"
                className="button"
                onClick={(e) => {
                  // 阻止事件冒泡,避免触发标签编辑
                  e.stopPropagation();
                  this.handleDelete(item);
                }}
              >
                &times;
              </button>
            </div>
          ))}

          <input
            ref={this.inputRef}
            className={"input " + (this.state.error && " has-error")}
            value={this.state.value}
            placeholder="Type or paste email addresses and press `Enter`..."
            onKeyDown={this.handleKeyDown}
            onChange={this.handleChange}
            onPaste={this.handlePaste}
          />
        </div>

        {this.state.error && <p className="error">{this.state.error}</p>}
      </>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

2. 调整CSS样式(styles.css)

通过样式让容器模拟输入框,同时让标签与输入框自然排列:

/* 外层容器:模拟输入框外观 */
.input-container {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  min-height: 40px;
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

/* 容器在输入框获焦时的高亮效果 */
.input-container:focus-within {
  border-color: #007bff;
  box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}

/* 邮箱标签样式 */
.tag-item {
  background-color: #e9ecef;
  padding: 4px 8px;
  border-radius: 4px;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 14px;
  cursor: pointer;
}

/* 标签删除按钮 */
.tag-item .button {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 16px;
  padding: 0;
  line-height: 1;
  color: #6c757d;
}

.tag-item .button:hover {
  color: #343a40;
}

/* 输入框样式:移除边框,与容器融合 */
.input {
  border: none;
  outline: none;
  flex: 1;
  min-width: 150px;
  font-size: 14px;
  padding: 4px 0;
}

/* 错误状态下的输入框文字颜色 */
.input.has-error {
  color: #dc3545;
}

/* 错误提示文字 */
.error {
  color: #dc3545;
  font-size: 14px;
  margin-top: 4px;
}

关键修改说明

  • 容器布局:用input-container作为外层容器,通过flex布局实现标签与输入框横向排列,flex-wrap: wrap保证标签过多时自动换行。
  • 视觉模拟:给容器设置边框、圆角,通过:focus-within伪类实现输入框获焦时的容器高亮,模拟原生输入框交互。
  • 输入框融合:移除输入框的边框与轮廓,设置flex:1让它占据容器剩余空间,与容器融为一体。
  • 事件修复:删除按钮添加e.stopPropagation(),避免点击删除时触发标签的编辑事件。
  • 语法修复:修正原代码中handleKeyDown方法里的数组语法错误。

内容的提问来源于stack exchange,提问作者user15361826

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 17:09:32