vue 递归创建菜单_如何在Vue中创建类似中等的突出显示菜单

vue 递归创建菜单

by Taha Shashtari

由Taha Shashtari

如何在Vue中创建类似中等的突出显示菜单 (How to Create a Medium-Like Highlight Menu in Vue)

A cool feature in Medium is the highlight menu that pops up when you select some text. This menu contains buttons that allow you to perform certain actions on the selected text like highlight and share.

在一个很酷的功能是当你选择一些文本在弹出的菜单中的亮点。 此菜单包含一些按钮,这些按钮使您可以对所选文本执行某些操作,例如突出显示和共享。

If you like this feature and you want to have it in your site, I’m going to show you how to create a reusable component that enables this behavior on the text it contains.

如果您喜欢此功能,并且希望在您的网站中使用它,我将向您展示如何创建可重用的组件,以在其包含的文本上启用此行为。

You can try a live demo on CodePen:

您可以在CodePen上尝试现场演示:

View the CodePen here.

此处查看CodePen。

使用Vue CLI 3创建一个新项目 (Creating a new project with Vue CLI 3)

With Vue CLI 3 instant prototyping, we can rapidly run a Vue app with just a single *.vue file.

借助Vue CLI 3 即时原型 ,我们可以仅使用一个*.vue文件快速运行Vue应用程序。

Note that this is only used for creating prototypes, not for production.

请注意,这仅用于创建原型,而不用于生产。

First, make sure that you have this installed globally:

首先,请确保已在全球范围内安装了此软件:

npm install -g @vue/cli-service-global

npm install -g @vue/cli-service-global

In this app, we’ll only need two files: App.vue and Highlightable.vue.

在此应用程序中,我们仅需要两个文件: App.vueHighlightable.vue

Highlightable.vue is our reusable highlight menu component. And App.vue is the main page component.

Highlightable.vue是我们可重用的突出显示菜单组件。 而App.vue是主要的页面组件。

Create both files in any directory you want; then, run vue serve on App.vue.

在所需的任何目录中创建两个文件; 然后,在App.vue上运行vue serve

vue serve App.vue

实施App.vue (Implementing App.vue)

In App.vue, we'll add two paragraphs. One that can be highlighted, and one that can't.

App.vue中 ,我们将添加两个段落。 一种可以突出显示,另一种不能突出显示。

We’ll also import and use Highlightable.vue before even creating it. (This is helpful to see how we're going to use it.)

我们甚至会在创建之前导入并使用Highlightable.vue 。 (这有助于了解我们将如何使用它。)

Here’s how it should look in the end:

最终效果如下:

<template>  <div class="app">    <highlightable      @share="onShare"      @highlight="onHighlight"    >      <p>        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet at debitis deserunt, optio rem eaque obcaecati non possimus nisi assumenda architecto exercitationem dolore quo praesentium, deleniti reiciendis sed ab nihil!      </p>    </highlightable>    <p>      <strong>This paragraph can't be highlighted.</strong> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Labore ipsam repellat, fugiat aut ex incidunt ut quisquam quasi consequatur ducimus quo in, cum soluta eos dolores tempore unde voluptate modi.    <;/p&gt;  </div></template&gt;<script>import Highlightable from './Highlightable'export default {  components: { Highlightable },  methods: {    onShare (text) {      console.log('share:', text)    },    onHighlight (text) {      console.log('highlight:', text)    }  }}</script><style scoped>* {  box-sizing: border-box;}.app {  width: 800px;  margin: 40px auto;  padding: 10px;  font-family: Verdana;  color: #333;  width: 100%;}p {  line-height: 1.5;}</style>

As you can see above, we're handling two events from Highlightable. These two events are the actions of the buttons in the highlight menu. These are just examples. You can change them to whatever you want.

正如您在上面看到的,我们正在处理Highlightable中的两个事件。 这两个事件是高亮菜单中按钮的动作。 这些仅仅是示例。 您可以将它们更改为任何您想要的。

实施Highlightable.vue (Implementing Highlightable.vue)

The template section consists of two parts: the menu element with buttons and <slot/> to display the text.

模板部分由两部分组成:带按钮的菜单元素和用于显示文本的<slo t />。

Let’s start with this code in the template:

让我们从模板中的以下代码开始:

<template>  <div>    <div      v-show="showMenu"      class="menu"    >      <span class="item">        Share      </span>      <span class="item">        Highlight      </span>      <!-- You can add more buttons here -->    </div>    <!-- The insterted text should be displayed here -->    <slot/>  </div></template>

Note that we're using showMenu, which we haven't created yet, to determine if we should display the menu.

请注意,我们使用尚未创建的showMenu来确定是否显示菜单。

Now let's move to the styling part.

现在,让我们转到样式部分。

Add the following CSS to <style> section:

将以下CSS添加到<sty le>部分:

&lt;style scoped>.menu {  height: 30px;  padding: 5px 10px;  background: #333;  border-radius: 3px;  position: absolute;  top: 0;  left: 0;  transform: translate(-50%, -100%);  transition: 0.2s all;  display: flex;  justify-content: center;  align-items: center;}.menu:after {  content: '';  position: absolute;  left: 50%;  bottom: -5px;  transform: translateX(-50%);  width: 0;  height: 0;  border-left: 6px solid transparent;  border-right: 6px solid transparent;  border-top: 6px solid #333;}.item {  color: #FFF;  cursor: pointer;}.item:hover {  color: #1199ff;}.item + .item {  margin-left: 10px;}&lt;/style>

Nothing is too complex here. .menu is for the highlight menu. menu:after is for the little triangle (arrow) in the bottom center of the menu.

这里没有什么太复杂的。 .menu用于突出显示菜单。 menu:aftermenu:after底部中心的小三角形(箭头)。

One important thing to note here is that .menu has an absolute position. We need this to position it above the selected text.

这里要注意的重要一件事是.menu具有absolute位置。 我们需要将其放置在所选文本上方。

Finally, let's move to the <script> section.

最后,让我们进入<scri pt>部分。

Let's start with the data.

让我们从data开始。

export default {  data () {    return {      x: 0,      y: 0,      showMenu: false,      selectedText: ''    }  }}
  • x and y are for positioning the menu.

    xy用于放置菜单。

  • showMenu to show/hide the menu.

    showMenu显示/隐藏菜单。

  • selectedText will contain the actual content of the selected text.

    selectedText将包含所选文本的实际内容。

Now, let's move to computed.

现在,让我们开始计算

computed: {  highlightableEl () {    return this.$slots.default[0].elm  }}

We only have a single computed property that returns the element used in the slot section of Highlightable. In our example, it would be the <;p> tag between <highlightable></highlightable>.

我们只有一个计算属性,该属性返回Highlightable的插槽部分中使用的元素。 在我们的示例中,将是tween <highlightable>< / highlightable>之间的< ; p>标记。

Then, let's add mounted and beforeDestroy hook functions.

然后,让我们添加mountedbeforeDestroy挂钩函数。

mounted () {  window.addEventListener('mouseup', this.onMouseup)},beforeDestroy () {  window.removeEventListener('mouseup', this.onMouseup)}

We use these to listen for mouseup event, which we handle inside onMouseup method.

我们使用它们来监听mouseup事件,该事件在onMouseup方法内部处理。

Now, let's create onMouseup method.

现在,让我们创建onMouseup方法。

methods: {  onMouseup () {    const selection = window.getSelection()    const selectionRange = selection.getRangeAt(0)    // startNode is the element that the selection starts in    const startNode = selectionRange.startContainer.parentNode    // endNode is the element that the selection ends in    const endNode = selectionRange.endContainer.parentNode    // if the selected text is not part of the highlightableEl (i.e. <p>)    // OR    // if startNode !== endNode (i.e. the user selected multiple paragraphs)    // Then    // Don't show the menu (this selection is invalid)    if (!startNode.isSameNode(this.highlightableEl) || !startNode.isSameNode(endNode)) {      this.showMenu = false      return    }    // Get the x, y, and width of the selection    const { x, y, width } = selectionRange.getBoundingClientRect()    // If width === 0 (i.e. no selection)    // Then, hide the menu    if (!width) {      this.showMenu = false      return    }    // Finally, if the selection is valid,    // set the position of the menu element,    // set selectedText to content of the selection    // then, show the menu    this.x = x + (width / 2)    this.y = y + window.scrollY - 10    this.selectedText = selection.toString()    this.showMenu = true  }}

Now let's update the template of Highlightable.vue to reflect the new changes.

现在,让我们更新Highlightable.vue的模板以反映新的更改。

<template>  <div>    <div      v-show="showMenu"      class="menu"      :style="{        left: `${x}px`,        top: `${y}px`      }"      @mousedown.prevent=""    >      <span        class="item"        @mousedown.prevent="handleAction('share')"      >        Share      </span>      <span        class="item"        @mousedown.prevent="handleAction('highlight')"      >        Highlight      </span>      <!-- You can add more buttons here -->    </div>    <!-- The insterted text should be displayed here -->    <slot/>  </div></template>

The changes are:

更改为:

  • Applied the positions to the menu element.

    将位置应用于菜单元素。
  • Added @mousedown.prevent="" to the menu element to prevent the menu from closing when clicking inside it.

    在菜单元素中添加了@mousedown.prevent="" ,以防止在内部单击时关闭菜单。

  • Added @mousedown.prevent="handleAction('share')" on share button to handle the clicked action. The same is for the highlight action.

    在共享按钮上添加了@mousedown.prevent="handleAction('share')"来处理单击的操作。 高亮操作也是如此。

Note that we're using mousedown event instead of click to prevent the text from getting unselected — which would cause the menu to close.

请注意,我们使用mousedown事件而不是click事件来防止未选中文本-这将导致菜单关闭。

The last thing we have to do is add the handleAction method.

我们要做的最后一件事是添加handleAction方法。

handleAction (action) {  this.$emit(action, this.selectedText)}

This method emits the action event and passes the selected text along with it. (We used this event in App.vue, remember?)

此方法发出action事件,并将所选文本与之一起传递。 (我们在App.vue中使用了此事件,还记得吗?)

With that, we're done! Now you have a reusable component that you can use to show a highlight menu for the selected text, just like Medium does.

这样,我们就完成了! 现在,您有了一个可重用的组件,可以像Medium一样使用它来显示所选文本的突出显示菜单。

Thanks for reading! By the way, I’m writing a book on how to build a complete single-page application from scratch using Vue. Check out the book’s landing page if you’re interested in learning more about what the book will cover:

谢谢阅读! 顺便说一句,我正在写一本关于如何使用Vue从头构建完整的单页应用程序的书。 如果您有兴趣了解有关该书涵盖内容的更多信息,请查看该书的登录页面:

翻译自: https://www.freecodecamp.org/news/how-to-create-a-medium-like-highlight-menu-in-vue-dc515f2dddef/

vue 递归创建菜单

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值