Interactive demos powered by a jsRibbon runtime matching the published library — every binding shown here behaves exactly as documented.
Every data-bind needs a component: ancestor. See exactly what happens when
one's missing, and how jsRibbon reacts when two instances of the same component disagree on markup.
<!-- No component: wrapper — silently does nothing, no warning --> <span data-bind="text:label">Ignored</span> <!-- Wrapped — fully reactive --> <div data-bind="component:ScopeInjected"> <span data-bind="text:label">Live</span> </div>
Two instances of the same named component must share identical inner HTML.
jsRibbon.hardFail controls what happens when they don't:
false (default) warns and keeps going, true throws.
<!-- same component name, different inner markup --> <div data-bind="component:Card"><span data-bind="text:v">A</span></div> <div data-bind="component:Card"><span data-bind="text:v">A</span><em>extra</em></div> <script> jsRibbon.hardFail = false; // default: console.warn, page keeps running jsRibbon.hardFail = true; // throws instead </script>
<div data-bind="component:TextComponent"> <span data-bind="text:name"></span> </div>
<div data-bind="component:TextComponent"> <span data-bind="text:name">Raheel Shan</span> </div>
<div data-bind="component:TextComponent"> <span data-bind="text:name">Raheel Shan</span> <br /> <label data-bind="text:name"></label> </div>
Two-way binding for text inputs, number spinners, and select dropdowns.
<div data-bind="component:AgeSelector"> <input type="number" min="0" max="120" value="25" data-bind="value:age, update:input" /> <span data-bind="text:age"></span> </div> <div data-bind="component:CityPicker"> <select data-bind="value:city"> <option value="">Select</option> <option value="Karachi">Karachi</option> </select> <span data-bind="text:city"></span> </div>
Conditionally apply CSS classes based on reactive state values.
<style> .box-highlighted { border-color: #2BEBA0 !important; } .box-large { transform: scale(1.1); } </style> <div data-bind="component:BoxCtrl"> <label> <input type="checkbox" data-bind="value:isGlowing"/> Highlight </label> <label> <input type="checkbox" data-bind="value:isBig"/> Scale </label> <div data-bind="class: [ isGlowing => box-highlighted, isBig => box-large ]" class="demo-box" >Reactive Box</div> </div>
Reactively set HTML attributes from state, using stateKey => attrName syntax.
<div data-bind="component:LinkCtrl"> <input data-bind="value:linkUrl, update:input" value="https://example.com" /> <input data-bind="value:linkTitle, update:input" value="Tooltip text" /> <a data-bind="attr: [ linkUrl => href, linkTitle => title ]"> Hover for tooltip, click to open </a> </div>
Show or hide elements by toggling display:none based on a boolean state key.
<div data-bind="component:ToggleDemo"> <label> <input type="checkbox" data-bind="value:isVisible" /> Show panel </label> <div data-bind="visible:isVisible"> <p> This panel is controlled by the checkbox above! </p> </div> </div>
Bind DOM events to component methods. data-* attributes are automatically parsed and passed as
the second argument.
jsRibbon.component('EventDemo', ($state) => { const handleClick = (event, data) => { $state.lastClicked = data.label; $state.price = data.price; }; return { handleClick }; }); <div data-bind="component:EventDemo"> <button data-bind="click:handleClick" data-label="Apple" data-price="1.2" >Apple $1.20</button> <button data-bind="click:handleClick" data-label="Mango" data-price="2.5" >Mango $2.50</button> </div>
A child component reads the parent's state and calls the parent's methods via a dotted key — no extra wiring needed.
jsRibbon.component('LayoutDemo', ($state) => { const save = () => { $state.lastSaved = new Date().toLocaleTimeString(); }; return { save }; }); jsRibbon.component('HeaderDemo', () => ({})); <div data-bind="component:LayoutDemo"> <input data-bind="value:title, update:input" /> <div data-bind="component:HeaderDemo"> <h1 data-bind="text:layoutDemo.title"></h1> <button data-bind="click:layoutDemo.save">Save</button> </div> </div>
Render lists from arrays. Rows are patched in place on mutation, and every binding type works inside a
row — including a two-way value: and a class: on the row itself, both shown
below.
jsRibbon.component('UserList', ($state) => ({ add: () => $state.users.push({ firstName: 'New User', age: 25, active: false }) })); <div data-bind="component:UserList"> <tbody data-bind="foreach:[data:users,as:user]" > <tr data-bind="class: [ active => active-row ]"> <td data-bind="text:firstName"> Raheel</td> <td data-bind="text:age">40</td> <td><input type="checkbox" data-bind="value:active"/></td> <td><button data-bind="click:removeItem" >✕</button></td> </tr> </tbody> <button data-bind="click:add"> Add User </button> </div>
| Name | Age | Active | |
|---|---|---|---|
| Raheel Shan | 40 | ||
| Saleem Ahmad | 21 |
Single checkboxes bind to booleans; multiple with same key bind to arrays. Radios bind to a string value.
<!-- Checkbox group --> <div data-bind="component:SkillPicker"> <label> <input type="checkbox" data-bind="toggle:skills" /> All </label> <label> <input type="checkbox" value="HTML" data-bind="value:skills" /> HTML </label> <span data-bind="text:skills"></span> </div> <!-- Radio buttons --> <div data-bind="component:GenderSelector"> <label> <input type="radio" name="gender" value="male" data-bind="value:gender" checked /> Male </label> </div>
Bind innerHTML from state. Edit the textarea to see the rendered output update in real time.
<div data-bind="component:HTMLChecker"> <textarea name="content" data-bind="value:details, update:input" > <b>Hello</b> <i>world!</i> </textarea> <div data-bind="html:details" ></div> </div>
jsRibbon's MutationObserver automatically registers components injected after page load — no manual rebind needed.
// Component registered before markup exists jsRibbon.component('DynamicComp', ($state) => { const bump = () => $state.count++; return { bump }; }); // Inject HTML later (simulating AJAX response) setTimeout(() => { container.innerHTML = ` <div data-bind="component:DynamicComp"> <span data-bind="text:count">0</span> <button data-bind="click:bump">+1</button> </div> `; }, 1500); <!-- No manual re-init required! -->