概述
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中创建类似中等的突出显示菜单 如何在Vue中创建类似中等的突出显示菜单 (How to Create a Medium-Like Highlight Menu in Vue)的全部内容,希望文章能够帮你解决vue 递归创建菜单_如何在Vue中创建类似中等的突出显示菜单 如何在Vue中创建类似中等的突出显示菜单 (How to Create a Medium-Like Highlight Menu in Vue)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复