Any JavaScript expression works inside the attribute: arithmetic, ternaries, method calls, template-free string concatenation. The binding re-runs whenever the component's state changes.
The safe pattern for rich previews is escape first, then decorate: run user text through $.escape(), then add your own tags for hashtags or mentions. That way the only HTML that reaches the page is HTML you wrote.
Preview (kj-html)
View the source for this demo
<div kj-data="composer">
<label for="cp-msg">Your post</label>
<textarea id="cp-msg" rows="3" kj-model="msg" maxlength="400" style="margin-top:6px"></textarea>
<p class="row" style="justify-content:space-between;margin:8px 0 16px">
<span class="muted" kj-text="words() + ' words'"></span>
<span :class="left() < 0 ? 'no' : left() < 20 ? 'partial' : 'muted'" kj-text="left() + ' characters left'" aria-live="polite"></span>
</p>
<h4 style="margin:0 0 8px">Preview <small class="muted">(kj-html)</small></h4>
<div class="cp-preview" kj-html="preview()"></div>
</div>$.component('composer', () => ({
msg: 'Shipping #jskjs today — one script, four superpowers. Thanks @kchecker! <b>This tag stays text.</b>',
left() { return 140 - this.msg.length; },
words() { return this.msg.trim() ? this.msg.trim().split(/\s+/).length : 0; },
preview() {
return $.escape(this.msg) // 1. escape everything the user typed
.replace(/#(\w+)/g, '<mark>#$1</mark>') // 2. then add markup we control
.replace(/@(\w+)/g, '<a href="https://github.com/$1" rel="noopener">@$1</a>');
},
}));More examples
Common bindings
<span kj-text="count"></span>
<p kj-text="user ? 'Hi ' + user.name : 'Please sign in'"></p>
<ul kj-html="items.map(i => '<li>' + $.escape(i) + '</li>').join('')"></ul>API
Bind textContent or innerHTML to an expression. Prefer kj-text for anything user-supplied.
Migrating
x-text → kj-text, x-html → kj-html.Related features
Reactive state
Declare state on an element with kj-data and every binding inside updates when it changes.
Open →PulseTwo-way binding
Bind form controls to state in both directions with kj-model — text, number, range, checkbox, radio and select.
Open →PulseAttribute binding
Bind any attribute to state with :attr or kj-bind:attr — src, alt, aria-*, disabled, style.
Open →