DEV Community

Son Goku
Son Goku

Posted on

Building Context Menu in Vue.js

Today we are going to create our context menu with VueJS from scratch, with traditional "Hello World" example.

Alt Text

Create fresh Vue Project

Let's start by creating new project and running the application.

The first you have to do is to install the Vue CLI on your computer

To create a new project, run:

vue create <enter the app name>

More about Vue cli, you can see here from their official docs.

It will generate new Project with following initial file structure:

  • index.html
  • src/App.vue
  • src/main.js
  • src/assets/logo.png
  • src/components/HelloWorld.vue

Start Project

Now lets run our generated project by Vue CLI

cd <project-name>
npm install
npm run serve

The Vue Instance

We will open now App.vue there will be following code:

<div id="app">
  <img width="25%" src="./assets/logo.png">
  <HelloWorld/>
</div>

For now we can remove HelloWorld component and create new component named ContextMenu.vue

Open you ContextMenu.vue and add basic template

<div class="context-menu" ref="context" tabindex="0">
        <slot></slot>
    </div>

A bit about code
Slot will allow us to render components inside of our context menu, and tabIndex prop will focus element

Add Styles

<style>
.context-menu {
    position: fixed;
    background: white;
    z-index: 999;
    outline: none;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
    cursor: pointer;
}
</style>

Now add some logic and full component Code

<template>
<div class="context-menu" v-show="show" :style="style" ref="context" tabindex="0" @blur="close">
        <slot></slot>
    </div>
</template>
<script>
import Vue from 'vue';

export default {
    name: 'CmpContextMenu',
    props: {
        display: Boolean, // prop detect if we should show context menu
    },
    data() {
        return {
            left: 0, // left position
            top: 0, // top position
            show: false, // affect display of context menu
        };
    },
    computed: {
        // get position of context menu
        style() {
            return {
                top: this.top + 'px',
                left: this.left + 'px',
            };
        },
    },
    methods: { 
        // closes context menu 
        close() {
            this.show = false;
            this.left = 0;
            this.top = 0;
        },
        open(evt) {
            // updates position of context menu 
            this.left = evt.pageX || evt.clientX;
            this.top = evt.pageY || evt.clientY;
            // make element focused 
            // @ts-ignore
            Vue.nextTick(() => this.$el.focus());
            this.show = true;
        },
    },
};
</script>
<style>
.context-menu {
    position: fixed;
    background: white;
    z-index: 999;
    outline: none;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
    cursor: pointer;
}
</style>

The code is self self explanatory explained open method opens context menu, close method closes the context menu, and computed propert styles that will return position of context menu.

Usage

Now import your new component into App.vue, and start show your own context menu.

<div id="app">
  <img width="25%" src="./assets/logo.png">
 <context-menu :display="showContextMenu" ref="menu">
      <ul>
         <li> List item 1 </li>
         <li> List item 2 </li>
      </ul>
</context-menu>

<button @click='openContextMenu'>activate context menu</button>
</div>

<script>
import ContextMenu from './ContextMenu';


export default {
  components: {
    ContextMenu,
  },
  data() {
     return { showContextMenu: false }
  },
  methods: {
    openContextMenu(e) {
         this.$refs.menu.open(e);
    }
  }
}
</script>

With a little edit you could create something looking like this:

Alt Text

Note

This is just a basic and really simple example how you could create context menu with as little code as possible.

As is almost always the case, you'll need to pick the approach that makes the most sense for your use-case.

Latest comments (5)

Collapse
 
farshidgh70 profile image
farshidgh70 • Edited

Thank you for this article. You need an edit. you should add pageYOffset when user scroll to down.
this.top = (evt.pageY || evt.clientY) - window.pageYOffset;

Collapse
 
dzun_n profile image
Dzun N

Nice Tips ❤

Collapse
 
muphet profile image
Muphet

what is the use of prop display in this?

Collapse
 
levirs565 profile image
Levi Rizki Saputra

Great

Collapse
 
hunterjsbit profile image
Son Goku

thank you :)