Building Context Menu in Vue.js
Tags: javascript, vue, webdev
Published: 2020-10-12 23:09:47

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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d7uvouaxazje9gohynyq.jpg)

# **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](https://cli.vuejs.org/guide/installation.html) on your computer

To create a new project, run:

```vue create <enter the app name>```

More about Vue cli, you can see [here](https://cli.vuejs.org/guide/creating-a-project.html#vue-create) 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](https://dev-to-uploads.s3.amazonaws.com/i/xk6ezvoukxrxp3nwifyx.png)

> 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.