如何判断contenteditable光标是否在{ }内及实现CSS编辑器自动缩进
Hey there! I’ve built similar code editor features before, so let’s break this down into two key parts: checking if the cursor is inside curly braces, and adding automatic tab indentation when hitting Enter. Here’s how to make it work reliably.
{ } 范围内 The core challenge here is handling nested braces (like in nested CSS rules or media queries) and making sure we’re checking the correct pair. We’ll use the browser’s Selection and Range APIs to get the cursor position, then scan the text to find the matching brace pair.
Here’s a robust function to do this:
function isCursorInsideBraces(editableElement) { const selection = window.getSelection(); if (selection.rangeCount === 0) return false; const range = selection.getRangeAt(0); const fullText = editableElement.textContent; const cursorPos = range.startOffset; // Step 1: Find the nearest unclosed opening brace before the cursor let openBraceIndex = -1; let closeBraceCounter = 0; for (let i = cursorPos - 1; i >= 0; i--) { if (fullText[i] === '}') { closeBraceCounter++; } else if (fullText[i] === '{') { if (closeBraceCounter === 0) { openBraceIndex = i; break; } else { closeBraceCounter--; } } } if (openBraceIndex === -1) return false; // Step 2: Find the matching closing brace after the cursor let closeBraceIndex = -1; let openBraceCounter = 0; for (let i = cursorPos; i < fullText.length; i++) { if (fullText[i] === '{') { openBraceCounter++; } else if (fullText[i] === '}') { if (openBraceCounter === 0) { closeBraceIndex = i; break; } else { openBraceCounter--; } } } // Return true if we found a matching pair and cursor is between them return closeBraceIndex !== -1 && cursorPos > openBraceIndex && cursorPos < closeBraceIndex; }
This function counts nested braces to avoid false positives—so it won’t think you’re inside an outer brace pair when you’re actually in a nested one.
Once we can detect the cursor’s position, we’ll listen for the Enter key press, block the default behavior, and insert a newline plus tab (or spaces, if you prefer) instead.
Add this event listener to your contenteditable element:
const cssEditor = document.querySelector('[contenteditable="true"]'); cssEditor.addEventListener('keydown', (e) => { if (e.key === 'Enter') { // Check if we're inside curly braces first if (isCursorInsideBraces(cssEditor)) { e.preventDefault(); // Stop the default newline behavior const selection = window.getSelection(); const range = selection.getRangeAt(0); // Define your indentation (use ' ' for 2 spaces if needed) const indent = '\t'; const newLineWithIndent = '\n' + indent; // Insert the custom newline + indent range.deleteContents(); const textNode = document.createTextNode(newLineWithIndent); range.insertNode(textNode); // Move the cursor to the end of the inserted text range.setStartAfter(textNode); range.setEndAfter(textNode); selection.removeAllRanges(); selection.addRange(range); } } });
This will insert a tab immediately after the newline, and place the cursor right after the indent—perfect for writing CSS rules.
A few edge cases to test and tweak if needed:
- Empty braces: If your cursor is between
{and}, hitting Enter should still insert the indent (the code above handles this fine). - Cursor at brace edges: If the cursor is right after
{or right before}, theisCursorInsideBracesfunction will still returntrue(which is correct). - Custom indentation: Swap
'\t'for' '(four spaces) if your team uses space-based indentation.
内容的提问来源于stack exchange,提问作者Seann

