Reputation: 3001
I am trying to add an extremely simple "syntax highlight" to a text input in svelte.
Basically what I want is to highlight everything in the input that matches the regex /{.*?}/
in a different color, but I can't figure out how.
my latest experiment looks like this
<script>
export let value;
let editor;
function update() {
value = editor.textContent
}
</script>
<div contenteditable="true"
bind:this={editor} on:input={update}>
{#if value}
{#each value.split(" ") as part, i}
<span class={part.match(/{.*?}/gm) ? "text-nord-12" : "text-nord-4"}
>{#if i !== 0} {/if}{part}</span
>
{/each}
{/if}
</div>
and while it isn't perfect it sort of works, except that the cursor gets reset from time to time when editing, which I assume is because the editor content is getting replaced when updating.
So basically is there any way to bind the text value while still being able to dynamically add/remove html tags?
I can note as well that binding textContent
does seem to work either as it removes all the highlighting when mounted... or I might just be doing something wrong with the binding
Upvotes: 1
Views: 1716
Reputation: 11706
Will this solve your problem using a use:action, on:blur and a replacer?
With blur you have to leave (lose focus) the div to mark the div text, but the cursor will nog jump on every keystroke.
Code and repl: Highlight matched text in a contenteditable div with svelte
<script>
let match = "svelte|input"; // pseudo rex
let content = "Adding syntax highlight to an input in svelte using a svelte action";
let editor;
function updateContent() {
if (editor) content = editor.textContent;
}
function marker(txt, rex) {
function markTerm(term) {
let cls = "red";
return ` <mark class="no-clicks ${cls}">${term}</mark> `;
}
return txt.replace(rex, (term) => markTerm(term));
}
export function highlight(node, [val, text]) {
function action() {
console.log(text);
if (text) node.innerHTML = marker(text, new RegExp(val, "g"));
}
action();
return {
update(obj) {
[val, text] = obj;
action();
}
};
}
</script>
<div>
<div contenteditable="true" use:highlight={[match, content]}
bind:this={editor} on:blur={updateContent} />
</div>
<style>
:global(.red) {
color: red;
}
</style>
Upvotes: 2