如何实现支持插入可删除气泡组件的输入框功能?
<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 acontenteditablecontainer 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">×</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

