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.vue和Highlightable.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> </div></template><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 <slo
t/> 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 <sty
le> section:
将以下CSS添加到<sty
le>部分:
<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;}</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:after
是menu: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 <scri
pt> section.
最后,让我们进入<scri
pt>部分。
Let's start with the data.
让我们从data开始。
export default { data () { return { x: 0, y: 0, showMenu: false, selectedText: '' } }}
x
andy
are for positioning the menu.x
和y
用于放置菜单。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.
然后,让我们添加mounted
和beforeDestroy
挂钩函数。
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中创建类似中等的突出显示菜单相关推荐
- 如何在mysql中创建过程_如何在MySQL 中创建存储过程?
问题阐述 自MySQL 5.0 开始,MySQL 就支持存储过程.存储过程是一些被用户定义的SQL 语句集合.一个存储程序是可以被存储在服务器中的一套SQL 语句.存储过程可以被程序.触发器或另一个存 ...
- 如何在mysql中创建连接_如何在MySQL中创建新用户并开启远程连接访问?
如何在MySQL中创建新用户并开启远程连接访问? 发布时间:2020-05-21 14:55:19 来源:亿速云 阅读:176 作者:鸽子 下面由mysql教程给大家介绍MySQL创建新用户并开启远程 ...
- vue在日历表上面创建事件_如何在R中创建颜色编码的日历
vue在日历表上面创建事件 用颜色编码的日历可以快速简便地查看您是否实现了日常目标. 您是否符合销售或社交媒体帖子等日常业务指标? 或者,您如何实现个人目标,例如每天锻炼? 乍一看,您可以了解自己的工 ...
- python中如何创建包_如何在Python中创建命名空间包?
TL:博士: 在Python3.3上,您不必做任何事情,只要不在名称空间包目录中放置任何__init__.py,它就可以工作了.在pre-3.3中,选择pkgutil.extend_path()解决方 ...
- python中怎么创建配置文件_如何在Django中创建配置文件注册表单?
我试图创建一个定制的注册表单,但是我不知道如何去做,因为我试图将默认的django注册与一个新的模型连接起来.在 这是它看起来的样子,可能是错的,但我正在考虑这样的事情.在 模型.pyclass Pr ...
- python动态创建字典_如何在Python中创建动态命名字典?
与其尝试动态生成一个动态数量的变量名,不如选择另一个更高级别的数据结构来存储对象,例如字典或列表.在import pandas as pd REFERENCE_CODE = ["ladder ...
- python怎么创建函数_如何在python中创建自己的map()函数
调用函数时,请使用星号*: def mapper(func, *sequences): result = [] if len(sequences) > 0: minl = min(len(sub ...
- wordpress创建_如何在WordPress中创建问卷(简易方式)
wordpress创建 Do you want to create a questionnaire in WordPress to survey your visitors or collect da ...
- wordpress创建_如何在WordPress中创建子页面
wordpress创建 Do you want to create a child page on your site? Pages in WordPress can be standalone or ...
最新文章
- perf + 火焰图分析程序性能
- Translating Embedding for Modeling Multi-relational Data
- logback配置(与log4j对比)
- android edittext不可复制_精选Android中高级面试题:性能优化,JNI,设计模式
- HTML 5 全局属性
- No execution.target specified in your configuration file.
- [html] 使用递归时应该注意哪些问题?
- MooTools教程(3):数组管理DOM元素
- ubuntu三种添加环境变量的方法
- Homebrew命令具体解释
- sql azure 语法_将SQL工作负载迁移到Microsoft Azure:规划迁移
- java 多个队列处理_加入多处理队列需要很长时间
- 系统安装-黑苹果之路
- python绘制余弦曲线散点图_python中如何用matlibplot画正弦曲线?
- 51nod 1005 1027 1029 高精度
- 全球四大互联网公司最大的敌人是谁
- Linux或Linux虚拟机桥接模式使用Python2认证Drcom
- 速魔与图马思特优缺点对比
- 利用扭力仪来检测特小公斤数电批输出扭矩
- 阿里云服务器带宽不够升级怎么收费?