JSFiddle - React, Tailwind, and code Playground

by snoopydaniels

HTML

<body>
  <ul class="menubar">
    <li>Home</li>
    <li>Contact Us</li>
    <li id="submenu">
      Submenu
    </li>
  </ul>
</body>

CSS

.menubar > li {
  display: inline-block;
  padding: 5px;
  height: 1em;
  position: relative;
}

div.dropDownMenu { 
  position: absolute;
  min-width: 100px;
  background-color: white;
}

div.dropDownMenu li {
  position: relative;
  padding: 5px;
  font-size: .8em;
}

.dropDownMenu ul {
  list-style-type: none;
  padding-left: 0px;
}

.dropDownMenu-hidden {
  display: none;
}

JavaScript

function Namespace() {
	return {};
}

var Widgets = Namespace();

Widgets.DropDownMenu = (function($){
	var defaultConfig = {
  	hideDelay: 300
  };
  var menuClass = 'dropDownMenu';
  	
	function DropDownMenu($container, model, config) {
  	this.$container = $container;
    this.model = model;
  	this.config = $.extend(defaultConfig, config);
    this.$markup = this.render(this.model);
    this.hideTimeout = null;
    $container.append(this.$markup);
    this.bindHoverEvents();
  }
  
  DropDownMenu.prototype.render = function(menuItems) {
  	var $menu = $('<div class="' + menuClass + '"><ul></ul></div>');
    var $ul = $menu.children();
    
    $menu.hide();
    
    for (var i = 0; i < menuItems.length; i++) {
    	var menuItem = menuItems[i];
    	var $li = $('<li></li>');
      $ul.append($li);
      
      $li.text(menuItem.text);
      
      if (menuItem.action) {
      	$li.click(this.createAction(menuItem.action, menuItem));
      }
      
      if (menuItem.submenu) {
      	var submenu = new DropDownMenu($li, menuItem.submenu, this.config);
        
        $li.data('dropDownMenu', submenu);
      }
    }
    
    return $menu;
  }
  
  DropDownMenu.prototype.createAction = function(action, menuItem) {
  	var self = this;
    
  	if (action instanceof Function) {
    	return function() {
      	action.call(self, menuItem);
      };
    } else if (this.router) {
    	return function() {
      	self.router.route(action);
      };
    } else {
    	return function() {
      	location.href = action;
      };
    }
  }
  
  function mouseEnter() {
  	this.show();
    
    if (this.hideTimeout) {
    	clearTimeout(this.hideTimeout);
      this.hideTimeout = null;
    }
  }
  
  function mouseLeave() {
  	var self = this;
    
    if (!this.$container.is(':hover') && !this.$markup.is(':hover')) 
      this.hideTimeout = setTimeout(function() {
        self.hide();
        self.hideTimeout = null;
      }, this.config.hideDelay);
  }
  
 ...