Cool Multi-level Context Menu With jQuery And CSS3

File Size: 30.7 KB
Views Total: 1583
Last Update:
Publish Date:
Official Website: Go to website
License: MIT
   
Cool Multi-level Context Menu With jQuery And CSS3

A pretty cool multi-level context menu to override the default browser right-click menu. Built using JavaScript (jQuery) and CSS/CSS3.

Features:

  • Pretty nice CSS3 animations.
  • Cool CSS filters.
  • Auto-reposition if it overflows the screen.
  • Theme switcher.
  • SEO-friendly. 

How to use it:

1. Create a multi-level context menu from nested HTML lists as follows:

<ul class="context">
  <li class="top">
    Menu 1
    <ul class="context sub"> 
      <li title="Chosen: Red">
        Menu 1-1
      </li>
      <li title="Chosen: Xtra Large">
        Menu 1-2
      </li>
    </ul>
  </li>
  <li class="hilight">
    Menu 2 (Highlighted)
  </li>
  <li>
    Menu 3
  </li>
  <!-- Separator -->
  <li class="div"></li>
  <li>
    Menu 4
    <ul class="context sub"> 
      <li class="hilight">
        Menu 4-1
      </li>
      <li>
        Menu 4-2
      </li>
    </ul>
  </li>
</ul>

2. Create two input fields for the theme switcher.

<div class="colors">
  <h4>Theme Switcher</h4>
  <input type="color" name="c1" id="c1" value="#673ab7"/>
  <input type="color" name="c2" id="c2" value="#3f51b5"/>
</div>

3. The required CSS rules for the context menu. Feel free to create your own styles by overriding the variables at the beginning of the following CSS snippets.

:root {
  --bg: #24262d;
  --text: #dfe3ff;
  --color1: #673ab7;
  --color2: #3f51b5;
  --divider: rgba(255,255,255,0.16);
}

.context {
  font-size: 0.875rem;
  color: var(--text);
  list-style: none;
  margin: 0;
  padding: 0.05em 0.25em;
  border: 1px solid transparent;
  border-right-color: rgba(255, 255, 255, 0.15);
  border-bottom-color: rgba(255, 255, 255, 0.15);
  border-left-color: rgba(0, 0, 0, 0.15);
  border-top-color: rgba(0, 0, 0, 0.15);
  border-radius: 5px;
  position: absolute;
  min-width: 16em;
  z-index: 1;
  background: linear-gradient(145deg, var(--color1), var(--color2));
  box-shadow: 2px 5px 16px -4px #141321;
  will-change: transform, opacity, filter;
  transition: transform, opacity, visibility, filter;
  transition-duration: 0.5s, 0.2s, 0.4s, 0.3s;
  transition-delay: 0.1s, 0s, 0.4s, 0.2s;
  transition-timing-function: ease;
  transform: rotate3d(-1, -1, 0, 30deg) scale(1);
  transform-origin: 0 0;
  opacity: 0;
  visibility: hidden;
  filter: blur(6px);
}
.context, .context * {
  -webkit-user-select: none;
     -moz-user-select: none;
      -ms-user-select: none;
          user-select: none;
  cursor: default;
}
.context.is-visible {
  opacity: 1;
  transform: none;
  transition-delay: 0s, 0s, 0s, 0s;
  visibility: visible;
  filter: none;
}
.context.sub {
  background: var(--color2);
  width: -webkit-max-content;
  width: -moz-max-content;
  width: max-content;
  min-width: 10em;
  left: 100%;
  top: -0.4em;
  transform: translateX(-0.7em);
  transition: transform, opacity, width, min-width, visibility;
  transition-timing-function: ease;
  transition-duration: 0.4s, 0.25s, 0.15s, 0.15s, 0.01s;
  transition-delay: 0.4s, 0.25s, 0.3s, 0.3s, 0.35s;
  overflow: hidden;
  filter: none;
}
.context.sub .f {
  transform: translateX(-2.25em);
}
.context.sub.oppositeX {
  right: 100%;
  left: auto;
  transform: translateX(0.7em);
}
.context.sub.oppositeY {
  top: auto;
  bottom: -0.4em;
}
.context > li {
  padding: 0.3em 1.5em 0.35em 2.8em;
  border-radius: 3px;
  position: relative;
}
.context > li:before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
  border-radius: 3px;
  z-index: -1;
  background-color: rgba(97, 97, 97, 0.37);
  mix-blend-mode: color-dodge;
  transition: opacity 0.15s cubic-bezier(0.55, 0.06, 0.68, 0.19);
  opacity: 0;
}
.context > li.hilight {
  font-weight: 500;
  padding-bottom: 0.5em;
  color: white;
}
.context > li:not(.context > li.nope):hover {
  color: white;
}
.context > li:not(.context > li.nope):hover:before {
  opacity: 1;
  transition: opacity 0.1s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.context > li:not(.context > li.nope):hover .sub {
  opacity: 1;
  transform: translateX(0);
  transition-delay: 0.2s, 0.25s, 0.2s, 0.2s, 0s;
  border-radius: 0 3px 3px 3px;
  visibility: visible;
}
.context > li:hover > .f, .context > li.hilight > .f {
  opacity: 1;
}
.context > li:last-child {
  margin-bottom: 0.25em;
}
.context > li:first-child {
  margin-top: 0.25em;
}
.context > li.nope {
  color: rgba(255, 255, 255, 0.3);
}
.context > li.active {
  -webkit-animation: flash 0.5s ease 1;
          animation: flash 0.5s ease 1;
}
.context > li:nth-of-type(1) {
  margin-top: 0.5em;
}
.context > li .f {
  opacity: 0.5;
  transition: all 0.2s ease;
}
.context > li i {
  font-style: normal;
  text-decoration: underline;
  -webkit-text-decoration-color: rgba(255, 255, 255, 0.35);
          text-decoration-color: rgba(255, 255, 255, 0.35);
}
.context .div {
  border-bottom: 1px solid var(--divider);
  padding: 0;
  margin-top: 0.3em;
  margin-bottom: 0.35em;
}
.context .f {
  font-style: normal;
  position: absolute;
  transform: translateX(-2.4em);
}
.context .f[class*=chevron-right] {
  right: 0;
  transform: none;
}

.f.f-circle {
  fill: red;
}

span.size {
  position: absolute;
  font-size: 0.675em;
  left: 1.2em;
  top: 0.8em;
  text-shadow: aliceblue;
}

@-webkit-keyframes flash {
  0% {
    background: rgba(255, 255, 255, 0);
  }
  7% {
    background: rgba(255, 255, 255, 0.2);
  }
  14% {
    background: rgba(255, 255, 255, 0);
  }
  21% {
    background: rgba(255, 255, 255, 0.3);
  }
}

@keyframes flash {
  0% {
    background: rgba(255, 255, 255, 0);
  }
  7% {
    background: rgba(255, 255, 255, 0.2);
  }
  14% {
    background: rgba(255, 255, 255, 0);
  }
  21% {
    background: rgba(255, 255, 255, 0.3);
  }
}

4. Load the necessary jQuery library at the end of the document.

<script src="/path/to/cdn/jquery.min.js"></script>

5. Load the necessary jQuery library at the end of the document.

$(function () {

  var $doc = $(document),
    $context = $(".context:not(.sub)"),
    $c1 = $("#c1"),
    $c2 = $("#c2");

  $doc.on("contextmenu", function (e) {

    var $window = $(window),
      $sub = $context.find(".sub");

    $sub.removeClass("oppositeX oppositeY");

    e.preventDefault();

    var w = $context.width();
    var h = $context.height();
    var x = e.clientX;
    var y = e.clientY;
    var ww = $window.width();
    var wh = $window.height();
    var padx = 30;
    var pady = 20;
    var fx = x;
    var fy = y;
    var hitsRight = (x + w >= ww - padx);
    var hitsBottom = (y + h >= wh - pady);

    if (hitsRight) {
      fx = ww - w - padx;
    }

    if (hitsBottom) {
      fy = wh - h - pady;
    }

    $context
      .css({
        left: fx - 1,
        top: fy - 1
      });

    var sw = $sub.width();
    var sh = $sub.height();
    var sx = $sub.offset().left;
    var sy = $sub.offset().top;
    var subHitsRight = (sx + sw - padx >= ww - padx);
    var subHitsBottom = (sy + sh - pady >= wh - pady);

    if (subHitsRight) {
      $sub.addClass("oppositeX");
    }

    if (subHitsBottom) {
      $sub.addClass("oppositeY");
    }

    $context.addClass("is-visible");

    $doc.on("mousedown", function (e) {

      var $tar = $(e.target);
      console.log($tar)

      if (!$tar.is($context) &&
        !$tar.closest(".context").length &&
        !$tar.is($c1) &&
        !$tar.is($c2)) {

        $context.removeClass("is-visible");
        $doc.off(e);

      }

    });

  });

  $context.on("mousedown touchstart", "li:not(.nope)", function (e) {

    if (e.which === 1) {

      var $item = $(this);

      $item.removeClass("active");

      setTimeout(function () {
        $item.addClass("active");
      }, 10);

    }

  });

  $c1.on("input change", function (e) {
    var color = $(this).val();
    document.body.style.setProperty("--color1", color);
  });

  $c2.on("input change", function (e) {
    var color = $(this).val();
    document.body.style.setProperty("--color2", color);
  });


});

This awesome jQuery plugin is developed by simeydotme. For more Advanced Usages, please check the demo page or visit the official website.