Bootstrap style zTree right click menu

  • 2021-07-22 08:28:27
  • OfStack

HTML:


<%--  Right-click menu  --%>
<div id="zTreeRightMenuContainer" style="z-index: 9999;">
 <%--  Hierarchy  0 --%>
 <ul class="dropdown-menu" role="menu" level="0">
      <%--  By adding styles to menu items " hasChildren "And in li You can extend the child menu by adding a menu structure under the tab  --%>
  <li class="hasChildren"><a tabindex="-1" action="refreshzTreeObj"> Refresh </a>
   <ul class="dropdown-menu" role="menu" level="1">
    <li><a tabindex="-1"> Copy the database to a different host / Database </a></li>
    <li><a tabindex="-1"> Create a database </a></li>
    <li><a tabindex="-1"> Change the database </a></li>
    <li><a tabindex="-1"> New data search </a></li>
    <li><a tabindex="-1"> Chuang / Build </a></li>
    <li><a tabindex="-1"> More database operations </a></li>
    <li class="divider"></li>
    <li><a tabindex="-1"> Backup / Export </a></li>
    <li><a tabindex="-1"> Import </a></li>
    <li class="divider"></li>
    <li><a tabindex="-1"> Creating a database schema HTML</a></li>
   </ul>
  </li>
 </ul>
 <%--  Hierarchy  1 --%>
 <ul class="dropdown-menu" role="menu" level="1">
  <li><a tabindex="-1"> Copy the database to a different host / Database </a></li>
  <li><a tabindex="-1"> Create a database </a></li>
  <li><a tabindex="-1"> Change the database </a></li>
  <li><a tabindex="-1"> New data search </a></li>
  <li><a tabindex="-1"> Chuang / Build </a></li>
  <li><a tabindex="-1"> More database operations </a></li>
  <li class="divider"></li>
  <li><a tabindex="-1"> Backup / Export </a></li>
  <li><a tabindex="-1"> Import </a></li>
  <li class="divider"></li>
  <li><a tabindex="-1"> Creating a database schema HTML</a></li>
 </ul>
 <%--  Hierarchy  2 --%>
 <ul class="dropdown-menu" role="menu" level="2">
  <li><a tabindex="-1"> Create a table </a></li>
  <li><a tabindex="-1"> Copy tables to different hosts / Database </a></li>
  <li><a tabindex="-1"> Data search </a></li>
  <li class="divider"></li>
  <li><a tabindex="-1"> Scheduled backup </a></li>
  <li><a tabindex="-1"> Backup table as SQL Dump </a></li>
 </ul>
</div>

CSS:


/*  Right-click menu  - start */
 .dropdown-menu .dropdown-menu {
  position: absolute;
  top: -9px;
  left: 100%;
 }
 .dropdown-menu li {
  position: relative;
 }
 .dropdown-menu li.hasChildren:before {
  content: '';
  position: absolute;
  top: 50%;
  right: 8px;
  width: 0;
  height: 0;
  margin-top: -5px;
  border-style: solid;
  border-color: transparent transparent transparent rgba(0, 0, 0, 0.5);
  border-width: 5px 0 5px 5px;
  pointer-events: none;
 }
 .dropdown-menu li.hasChildren:hover > .dropdown-menu {
  display: block;
 }
 /*  Right-click menu  - end */

JS:


/*  The following is the right-click menu plug-in ( Bootstrap Style)  */
;(function ($) {
 'use strict';
 /* CONTEXTMENU CLASS DEFINITION
  * ============================ */
 var toggle = '[data-toggle="context"]';
 var ContextMenu = function (element, options) {
  this.$element = $(element);
  this.before = options.before || this.before;
  this.onItem = options.onItem || this.onItem;
  this.scopes = options.scopes || null;
  if (options.target) {
   this.$element.data('target', options.target);
  }
  this.listen();
 };
 ContextMenu.prototype = {
  constructor: ContextMenu
  , show: function (e) {
   var $menu
    , evt
    , tp
    , items
    , relatedTarget = {relatedTarget: this, target: e.currentTarget};
   if (this.isDisabled()) return;
   this.closemenu();
   if (this.before.call(this, e, $(e.currentTarget)) === false) return;
   $menu = this.getMenu();
   $menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
   tp = this.getPosition(e, $menu);
   items = 'li:not(.divider)';
   $menu.attr('style', '')
    .css(tp)
    .addClass('open')
    .on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
    .trigger('shown.bs.context', relatedTarget);
   // Delegating the `closemenu` only on the currently opened menu.
   // This prevents other opened menus from closing.
   $('html')
    .on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
   return false;
  }
  , closemenu: function (e) {
   var $menu
    , evt
    , items
    , relatedTarget;
   $menu = this.getMenu();
   if (!$menu.hasClass('open')) return;
   relatedTarget = {relatedTarget: this};
   $menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
   items = 'li:not(.divider)';
   $menu.removeClass('open')
    .off('click.context.data-api', items)
    .trigger('hidden.bs.context', relatedTarget);
   $('html')
    .off('click.context.data-api', $menu.selector);
   // Don't propagate click event so other currently
   // opened menus won't close.
   if (e) {
    e.stopPropagation();
   }
  }
  , keydown: function (e) {
   if (e.which == 27) this.closemenu(e);
  }
  , before: function (e) {
   return true;
  }
  , onItem: function (e) {
   return true;
  }
  , listen: function () {
   this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this));
   $('html').on('click.context.data-api', $.proxy(this.closemenu, this));
   $('html').on('keydown.context.data-api', $.proxy(this.keydown, this));
  }
  , destroy: function () {
   this.$element.off('.context.data-api').removeData('context');
   $('html').off('.context.data-api');
  }
  , isDisabled: function () {
   return this.$element.hasClass('disabled') ||
    this.$element.attr('disabled');
  }
  , getMenu: function () {
   var selector = this.$element.data('target')
    , $menu;
   if (!selector) {
    selector = this.$element.attr('href');
    selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
   }
   $menu = $(selector);
   return $menu && $menu.length ? $menu : this.$element.find(selector);
  }
  , getPosition: function (e, $menu) {
   var mouseX = e.clientX
    , mouseY = e.clientY
    , boundsX = $(window).width()
    , boundsY = $(window).height()
    , menuWidth = $menu.find('.dropdown-menu').outerWidth()
    , menuHeight = $menu.find('.dropdown-menu').outerHeight()
    , tp = {"position": "absolute", "z-index": 9999}
    , Y, X, parentOffset;
   if (mouseY + menuHeight > boundsY) {
    Y = {"top": mouseY - menuHeight + $(window).scrollTop()};
   } else {
    Y = {"top": mouseY + $(window).scrollTop()};
   }
   if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) {
    X = {"left": mouseX - menuWidth + $(window).scrollLeft()};
   } else {
    X = {"left": mouseX + $(window).scrollLeft()};
   }
   // If context-menu's parent is positioned using absolute or relative positioning,
   // the calculated mouse position will be incorrect.
   // Adjust the position of the menu by its offset parent position.
   parentOffset = $menu.offsetParent().offset();
   X.left = X.left - parentOffset.left;
   Y.top = Y.top - parentOffset.top;
   return $.extend(tp, Y, X);
  }
 };
 /* CONTEXT MENU PLUGIN DEFINITION
  * ========================== */
 $.fn.contextmenu = function (option, e) {
  return this.each(function () {
   var $this = $(this)
    , data = $this.data('context')
    , options = (typeof option == 'object') && option;
   if (!data) $this.data('context', (data = new ContextMenu($this, options)));
   if (typeof option == 'string') data[option].call(data, e);
  });
 };
 $.fn.contextmenu.Constructor = ContextMenu;
 /* APPLY TO STANDARD CONTEXT MENU ELEMENTS
  * =================================== */
 $(document)
  .on('contextmenu.context.data-api', function () {
   $(toggle).each(function () {
    var data = $(this).data('context');
    if (!data) return;
    data.closemenu();
   });
  })
  .on('contextmenu.context.data-api', toggle, function (e) {
   $(this).contextmenu('show', e);
   e.preventDefault();
   e.stopPropagation();
  });
}(jQuery));


/*  The following method is used through the above js Method of plug-in encapsulation  */
/*
    parentNode ( zTree Container  ||  Specified node) 
*/
function initzTreeRightMenu(parentNode) {
 // Tree menu right-click event 
 $('li, a', $(parentNode)).contextmenu({
  target: '#zTreeRightMenuContainer', // This setting item is zTree Container of 
  before: function (e, element, target) {
   // Right-click the node currently ID
   var selectedId = element[0].tagName == 'LI' ? element.attr('id') : element.parent().attr('id');
   // According to the node ID Get the current node details 
   curSelectNode = zTreeObj.getNodeByTId(selectedId);
   // Hierarchy of the current node 
   var level = curSelectNode.level;
   level = 0;
   // Select the current right-click node 
   zTreeObj.selectNode(curSelectNode);
   // Displays the corresponding menu according to the current node level 
   $('#zTreeRightMenuContainer ul.dropdown-menu[level="' + level + '"]').removeClass('hide').siblings().addClass('hide');
  },
  onItem: function (context, e) {
   var action = $(e.target).attr('action');
   this.closemenu();
   if (action) {
    zTreeRightMenuFuns[action]();
   }
  }
 });
}

Steps:

1. Introduce js and css files related to zTree (take my own project as an example: jquery. ztree. all-3. 5. min. js, zTreeStyle. css);

2. Save the right-click menu plug-in given above as an js file introduction page (take my own project as an example: bsContextmenu. js)

3. After the page initializes zTree, call the above method: initzTreeRightMenu ('# schemaMgrTree'); //'# schemaMgrTree' is the zTree container for my own project ID

Remarks:

1. If there are asynchronous loaded nodes in zTree (take my own project as an example: some nodes in zTree are loaded after expanding the parent node, in this case, it is necessary to bind the child nodes of the current node in onExpandFun of zTree)


function onExpandFun(event, treeId, treeNode) {
  /*  Expand the code executed by the current node .... */// Bind the child node right-click event of the currently expanded node 
  initzTreeRightMenu('#' + treeNode.tId); //treeNode.tId  Object of the currently expanded node ID
}

Related articles: