// MVC Code function Observable(obj){ var observers = {}; this.observe = function(key, observer){ if(observers[key] === undefined) observers[key] = []; observers[key].push(observer); }; this.stopObserving = function(observer){ for(key in observers){ var i = 0; var ubounds = observers[key].length; for(i; i < ubounds; i++){ if(observers[key][i] === observer){ observers[key].splice(i, 1); if(observers[key].length === 0) delete observers[key]; break; } } } }; this.changed = function(key, old, v){ if(observers[key] === undefined) return; var i = 0; var ubounds = observers[key].length; for(i; i<ubounds; i++){ observers[key][i](key, old, v, this); } }; this.release = function(){ var key = null; for(key in observers){ var observer = null; while(observer = observers[key].pop()){} } }; if(obj){ for(var key in obj){ (function(m, key, obj){ Object.defineProperty(m, key, { get: function(){ return obj[key]; } , set: function(v){ var old = obj[key]; obj[key] = v; m.changed('value', old, v, m); } , enumerable: true }); })(this, key, obj); } } return this; } function View(container, controller, model){ this.container = container; this.controller = controller || new Controller(null, model); this.model = model; this.release = function(){ if(this.model){ this.model.stopObserving(this); } }; this.views = []; return this; } function Controller(delegate, model){ this.model = model; this.delegate = delegate; this.view = null; this.release = function(){ if(this.model){ this.model.stopObserving(this); } }; this.setView = function(v){ this.view = v; this.didSetView(v); }; this.didSetView = function(){}; return this; } // App code function RatingView(container, controller, model){ var self = View.apply(this, [container, controller, model]); var lis = container.querySelectorAll('li'); this.items = []; for(var i = 0; i < lis.length; i++){ this.items[i] = lis[i]; } function reset(){ self.items.forEach(function(item, i){ item.className = ''; }); } function ratingChanged(key, old, v){ reset(); for(var i = 0; i < v+1; i++){ self.items[i].className = 'filled'; } } this.controller.setView(this); this.model.observe('value', ratingChanged); return this; } function RatingController(delegate, model){ var self = Controller.apply(this, [delegate, model]); this.handleEvent = function(e){ this.view.items.forEach(function(item, i){ if(e.target === item) model.value = i; }); }; this.release = function(){ this.view.container.removeEventListener('click', this); }; this.didSetView = function(v){ this.view.container.addEventListener('click', this, true); }; return this; } var app = (function(){ var rating = new Observable({value: 0, max: 10}); var views = []; views.push(new RatingView(document.getElementById('fundoo-rating'), new RatingController(app, rating), rating)); views.push(new RatingView(document.getElementById('readonly-rating'), null, rating)); window.addEventListener('unload', function(){ views.forEach(function(v){ v.release(); }); }, true); })();
Rating is <span id="clickable-rating"></span> <br/> Clickable Rating <br/> <div id="fundoo-rating"> <ul class="rating"> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> </ul> </div> <br/> Readonly rating <br/> <div id="readonly-rating"> <ul class="rating"> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> <li>★</li> </ul> </div>
.rating{ color: #a9a9a9; margin: 0; padding: 0; } ul.rating { display: inline-block; } .rating li { list-style-type: none; display: inline-block; padding: 1px; text-align: center; font-weight: bold; cursor: pointer; } .rating .filled { color: #21568b; }