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

如何实现支持插入可删除气泡组件的输入框功能?

Can You Use an <input> Field for This?

Hey there! Great question—let’s break this down clearly:

Short Answer

  • Simple text insertion: Yes, a standard <input> field works perfectly for this.
  • Stylized bubble components: No, you can’t do this with a plain <input>—it only supports plain text, not HTML/CSS-styled elements. You’ll need an alternative like a contenteditable container or a rich text library.

1. Simple Text Insertion (Using <input>)

For inserting plain text like "Hello" or "Emma", <input> is ideal. The key is inserting text at the cursor position (not just appending it) and moving the cursor to the end of the inserted text. Here’s a working example:

HTML

<input type="text" id="textInput" placeholder="Type or click buttons...">
<button data-text="Hello">Insert Hello</button>
<button data-text="Emma">Insert Emma</button>

JavaScript

const input = document.getElementById('textInput');
const insertButtons = document.querySelectorAll('button[data-text]');

insertButtons.forEach(button => {
  button.addEventListener('click', () => {
    const textToInsert = button.dataset.text;
    const startPos = input.selectionStart;
    const endPos = input.selectionEnd;
    const currentValue = input.value;

    // Insert text at the cursor's current position
    input.value = `${currentValue.substring(0, startPos)}${textToInsert}${currentValue.substring(endPos)}`;

    // Move cursor to right after the inserted text
    input.selectionStart = input.selectionEnd = startPos + textToInsert.length;
    input.focus();
  });
});

This handles cases where the cursor is in the middle of existing text, not just at the end.


2. Complex Bubble Components (Alternative Solutions)

Since <input> can’t render styled elements, the most straightforward native solution is using a <div> with contenteditable="true". This lets you insert full HTML components (like your bubble with a close button and background color) and handle cursor positioning/backspace deletion.

Full Implementation

HTML

<div class="input-wrapper">
  <div id="editableContainer" contenteditable="true" placeholder="Type or insert bubbles..."></div>
  <button data-bubble-text="Hello">Insert Hello Bubble</button>
  <button data-bubble-text="Emma">Insert Emma Bubble</button>
</div>

CSS

.input-wrapper {
  margin: 1rem 0;
}

#editableContainer {
  border: 1px solid #ddd;
  padding: 0.5rem;
  min-height: 2.5rem;
  border-radius: 4px;
  font-size: 1rem;
}

/* Style for placeholder text */
#editableContainer:empty:before {
  content: attr(placeholder);
  color: #999;
}

.bubble {
  display: inline-block;
  background-color: #e3f2fd;
  padding: 0.25rem 0.5rem;
  border-radius: 1rem;
  margin: 0 0.25rem;
  position: relative;
  cursor: default;
  font-size: 0.9rem;
}

.bubble .close-btn {
  margin-left: 0.5rem;
  font-size: 0.8rem;
  cursor: pointer;
  color: #666;
}

.bubble .close-btn:hover {
  color: #000;
}

JavaScript

const editableContainer = document.getElementById('editableContainer');
const bubbleButtons = document.querySelectorAll('button[data-bubble-text]');

// Create a bubble element with close button
function createBubble(text) {
  const bubble = document.createElement('span');
  bubble.classList.add('bubble');
  bubble.innerHTML = `${text} <span class="close-btn">&times;</span>`;

  // Handle close button click
  bubble.querySelector('.close-btn').addEventListener('click', () => {
    bubble.remove();
    editableContainer.focus();
  });

  return bubble;
}

// Insert bubble at cursor position
function insertBubbleAtCursor(bubble) {
  const selection = window.getSelection();
  const range = selection.getRangeAt(0);

  // Delete any selected content first
  range.deleteContents();

  // Insert the bubble
  range.insertNode(bubble);

  // Move cursor to right after the bubble
  const newRange = document.createRange();
  newRange.setStartAfter(bubble);
  newRange.collapse(true);
  selection.removeAllRanges();
  selection.addRange(newRange);

  editableContainer.focus();
}

// Button click handlers
bubbleButtons.forEach(button => {
  button.addEventListener('click', () => {
    const bubbleText = button.dataset.bubbleText;
    const bubble = createBubble(bubbleText);
    insertBubbleAtCursor(bubble);
  });
});

// Handle backspace to delete entire bubble when cursor is right after it
editableContainer.addEventListener('keydown', (e) => {
  if (e.key === 'Backspace') {
    const selection = window.getSelection();
    const range = selection.getRangeAt(0);

    // Check if cursor is positioned right after a bubble
    const previousElement = range.startContainer.previousElementSibling;
    if (previousElement?.classList.contains('bubble') && range.startOffset === 0) {
      e.preventDefault(); // Stop default backspace behavior
      previousElement.remove();

      // Reset cursor to where the bubble was
      const newRange = document.createRange();
      newRange.setStart(range.startContainer, 0);
      newRange.collapse(true);
      selection.removeAllRanges();
      selection.addRange(newRange);
    }
  }
});

Key Features Handled Here:

  • Insert styled bubbles at the cursor position
  • Move cursor to after the bubble for continued typing
  • One-click deletion via the bubble’s close button
  • Backspace deletes the entire bubble when cursor is immediately to its right
  • Placeholder text for the editable container

Other Alternatives

If you need more advanced features (like collaborative editing or complex formatting), consider using a rich text library:

  • Quill.js: Lightweight, easy to customize, and supports custom blot types (perfect for your bubble components)
  • Slate.js: A highly flexible framework for building custom editors, great for complex use cases

But for your specific needs, the native contenteditable solution is lightweight and doesn’t require extra dependencies.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:22:18