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 递归创建菜单

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

  1. 如何在mysql中创建过程_如何在MySQL 中创建存储过程?

    问题阐述 自MySQL 5.0 开始,MySQL 就支持存储过程.存储过程是一些被用户定义的SQL 语句集合.一个存储程序是可以被存储在服务器中的一套SQL 语句.存储过程可以被程序.触发器或另一个存 ...

  2. 如何在mysql中创建连接_如何在MySQL中创建新用户并开启远程连接访问?

    如何在MySQL中创建新用户并开启远程连接访问? 发布时间:2020-05-21 14:55:19 来源:亿速云 阅读:176 作者:鸽子 下面由mysql教程给大家介绍MySQL创建新用户并开启远程 ...

  3. vue在日历表上面创建事件_如何在R中创建颜色编码的日历

    vue在日历表上面创建事件 用颜色编码的日历可以快速简便地查看您是否实现了日常目标. 您是否符合销售或社交媒体帖子等日常业务指标? 或者,您如何实现个人目标,例如每天锻炼? 乍一看,您可以了解自己的工 ...

  4. python中如何创建包_如何在Python中创建命名空间包?

    TL:博士: 在Python3.3上,您不必做任何事情,只要不在名称空间包目录中放置任何__init__.py,它就可以工作了.在pre-3.3中,选择pkgutil.extend_path()解决方 ...

  5. python中怎么创建配置文件_如何在Django中创建配置文件注册表单?

    我试图创建一个定制的注册表单,但是我不知道如何去做,因为我试图将默认的django注册与一个新的模型连接起来.在 这是它看起来的样子,可能是错的,但我正在考虑这样的事情.在 模型.pyclass Pr ...

  6. python动态创建字典_如何在Python中创建动态命名字典?

    与其尝试动态生成一个动态数量的变量名,不如选择另一个更高级别的数据结构来存储对象,例如字典或列表.在import pandas as pd REFERENCE_CODE = ["ladder ...

  7. python怎么创建函数_如何在python中创建自己的map()函数

    调用函数时,请使用星号*: def mapper(func, *sequences): result = [] if len(sequences) > 0: minl = min(len(sub ...

  8. wordpress创建_如何在WordPress中创建问卷(简易方式)

    wordpress创建 Do you want to create a questionnaire in WordPress to survey your visitors or collect da ...

  9. wordpress创建_如何在WordPress中创建子页面

    wordpress创建 Do you want to create a child page on your site? Pages in WordPress can be standalone or ...

最新文章

  1. perf + 火焰图分析程序性能
  2. Translating Embedding for Modeling Multi-relational Data
  3. logback配置(与log4j对比)
  4. android edittext不可复制_精选Android中高级面试题:性能优化,JNI,设计模式
  5. HTML 5 全局属性
  6. No execution.target specified in your configuration file.
  7. [html] 使用递归时应该注意哪些问题?
  8. MooTools教程(3):数组管理DOM元素
  9. ubuntu三种添加环境变量的方法
  10. Homebrew命令具体解释
  11. sql azure 语法_将SQL工作负载迁移到Microsoft Azure:规划迁移
  12. java 多个队列处理_加入多处理队列需要很长时间
  13. 系统安装-黑苹果之路
  14. python绘制余弦曲线散点图_python中如何用matlibplot画正弦曲线?
  15. 51nod 1005 1027 1029 高精度
  16. 全球四大互联网公司最大的敌人是谁
  17. Linux或Linux虚拟机桥接模式使用Python2认证Drcom
  18. 速魔与图马思特优缺点对比
  19. 利用扭力仪来检测特小公斤数电批输出扭矩
  20. 阿里云服务器带宽不够升级怎么收费?

热门文章

  1. 探索 Java 隐藏的开销
  2. 【面试必备】java写spark好不好
  3. 腾讯,字节,阿里,小米,京东大厂Offer拿到手软!讲的明明白白!
  4. 2019.03.20 mvt,Django分页
  5. 用php生成HTML文件的类
  6. 总结verilog产生随机数的$random和seed
  7. 在使用ToolBar + AppBarLayout,实现上划隐藏Toolbar功能,遇到了一个坑。
  8. 进程kswapd0与events/0消耗大量CPU的问题
  9. Android工程开发笔记一
  10. Holedox Moving