Live Examples

Interactive demos powered by a jsRibbon runtime matching the published library — every binding shown here behaves exactly as documented.

Component Scoping

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.

1. Bound inside a component vs. bound outside one

 scoping.html
<!-- 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>
Live demo
Click a button — the result (and any console warning) shows here.

2. Markup-consistency guard — softFail vs hardFail

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.

 scoping.html
<!-- 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>
Live demo
Click a button to see what jsRibbon does.

Text Binding

1. Basic Reactive Binding (No Initial DOM Value)

 text-binding.html
<div data-bind="component:TextComponent">  
    <span data-bind="text:name"></span>
</div>
Live demo

2. Reactive Binding With Initial Value From DOM

 text-binding.html
<div data-bind="component:TextComponent">  
    <span data-bind="text:name">Raheel Shan</span>
</div>
Live demo
Raheel Shan

3. Multiple Elements Sharing the Same Reactive Property

 text-binding.html
<div data-bind="component:TextComponent">  
    <span data-bind="text:name">Raheel Shan</span>
    <br />
    <label data-bind="text:name"></label>
</div>
Live demo
Raheel Shan

Value / Input Binding

Two-way binding for text inputs, number spinners, and select dropdowns.

 value-binding.html
<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>
Live demo
Number input
Age:
Select dropdown
Selected:

Class Binding

Conditionally apply CSS classes based on reactive state values.

 class-binding.html
<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>
Live demo
Reactive Box

Attribute Binding

Reactively set HTML attributes from state, using stateKey => attrName syntax.

 attr-binding.html
<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>
Live demo

Visible Binding

Show or hide elements by toggling display:none based on a boolean state key.

 visible.html
<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>
Live demo
✓ This panel appears when the checkbox is checked!

Event Bindings

Bind DOM events to component methods. data-* attributes are automatically parsed and passed as the second argument.

 events.html
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>
Live demo
Selected: none — $0

Composition

A child component reads the parent's state and calls the parent's methods via a dotted key — no extra wiring needed.

 composition.html
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>
Live demo
Title (edited here, in the parent)
Rendered by the child component, reading the parent:

Last saved: never

Foreach Binding

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.

 foreach.html
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>
Live demo
Name Age Active
Raheel Shan 40
Saleem Ahmad 21

Checkbox & Radio

Single checkboxes bind to booleans; multiple with same key bind to arrays. Radios bind to a string value.

 checkbox-radio.html
<!-- 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>
Live demo
Checkbox group with Select All
Selected:
Radio buttons
Selected:

HTML Binding

Bind innerHTML from state. Edit the textarea to see the rendered output update in real time.

 html.html
<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>
Live demo
Rendered output:

Dynamic Content / AJAX

jsRibbon's MutationObserver automatically registers components injected after page load — no manual rebind needed.

 dynamic-content.html
// 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! -->
Live demo
⏳ Injecting component in 2s…