JSFiddle - React, Tailwind, and code Playground

by badsyntax

HTML

<ul id="list" data-bind="foreach: items, click: onListClick">
  <li>
    <a href="#">
      <!-- ko text: name --><!-- /ko -->
      <span class="actions">
        <span class="add" title="Add">+</span>
        <span class="remove" title="Remove">X</span>
      </span>
    </a>
  </li>
</ul>

<!-- Testing closest() which checks ancestors -->
<div class="foo"></div>

CSS

#list {
  border: 1px solid #eee;
  width: 200px;
  margin: 1em;
}
#list li {
  overflow: hidden;
}
#list a {
  display: block;
}
#list a:hover {
  background: #ddd;
}
#list .actions {
  float: right;
}
#list .actions span {
  display: inline-block;
  width: 20px;
  border: 1px solid #ddd;
  text-align: center;
}
#list .actions span:hover {
  background: blue;
  color: white;
}

JavaScript

(function() {
  
  // Knockout event delegation idea, by Richard Willis
  // NOTE: not supported in < IE8
  
  var delegateEvent = (function() {    
    
    // You could refactor this to use $.fn.closest() to have 
    // better browser support.
    function matchesSelector(selector, e) {
      var all = e.currentTarget.querySelectorAll(selector);
      var l = all.length;
      var i = 0;
      for (i; i < l; i++) {
        if (all[i] === e.target) {
          return true;
        }
      }
      return false;
    }
    
    return function(e, handlers) {          
      for(var key in handlers) {         
        if (matchesSelector(key, e)) {
          handlers[key](ko.dataFor(e.target), e);
          break; // only match the first selector
        }
      }
    };    
  }());
    
  var ListViewModel = function () {
    
    this.items = ko.observableArray([{
      name: 'List item 1'
    }, {
      name: 'List item 2'
    }, {
      name: 'List item 3'
    }, {
      name: 'List item 4'
    }, {
      name: 'List item 5'
    }, {
      name: 'List item 6'
    }]);
    
    this.onListClick = function(vm, e) {
      
      e.preventDefault();

      // Delegate the click event to the appropriate handler
      delegateEvent(e, {        
        'a .add': this.onAddClick.bind(this),
        'a .remove': this.onRemoveClick.bind(this),
        'a': this.onAnchorClick.bind(this)
      });
    }
   
    this.onAnchorClick = function(model, e) {
      alert('Clicked on ANCHOR for: ' + model.name);
    };
    
    this.onAddClick = function(model, e) {
      alert('Clicked on ADD for: ' + model.name);
    };
    
    this.onRemoveClick = function(model, e) {
       alert('Clicked on REMOVE for: ' + model.name);
    };
  };
  
  ko.applyBindings(new ListViewModel(), document.getElementById('list'));  
}());