When Vue.js Can't Help You
Do you love Vue.js but hate how it isn't able to work at certain places in the DOM? Read on to learn how to use Vue to get around these issues.
Join the DZone community and get the full member experience.
Join For Freeif you want to build a web page with javascript, vue.js can do one helluva job on it. but there's a condition: it only works on parts of the page where it has unhampered control. any part that might be interfered with by other scripts or plugins is a no-go for vue.
this means the
head
and
body
tags are vue-free zones. it's a real bummer if you wanted vue to manage a class on the
body
, to take one example.
but while vue can't
directly
manage the
head
or
body
tags, it can still help you to manage them through other means.
vue's beef with the head and body tags
why is vue picky about where it works?
vue optimizes page rendering through the use of a virtual dom . this is a javascript representation of the "real" dom that vue keeps in memory. dom updates are often slow, so changes are made first to the virtual dom, allowing vue to optimize how it updates the real dom through batching, etc.
this system would be undermined if some third party were to make changes to the dom without vue's knowledge causing a mismatch between the real dom and the virtual dom.
for this reason, vue will not attempt to control the whole page, but only a part of the page where it knows it will have unhampered control.
the mount element
the first thing we usually do in a vue project is to give vue a mount element in the configuration object via the
el
property:
new vue({
el: '#app'
});
this tells vue where we've set aside part of the page that it can have to itself. vue will have dominion over this element and all of its children. but it is unable to affect any element
outside
of the mount element, be it sibling or ancestor:
xxxxxxxxxx
<head>
<!--vue has no power here!-->
</head>
<body>
<!--vue has no power here!-->
<div id="app">
<!--vue's dominion-->
</div>
<div id="not-the-app">
<!--vue has no power here!-->
</div>
</body>
no mounting to the
body
you'd be excused for thinking that the
body
tag would be a better place to mount since there are many good reasons to want to have control over
body
classes, body events, etc.
the problem is that there are browser plugins and third party scripts that pollute the
body
with their own classes, event listeners, and will even append their own child nodes willy-nilly.
that is just too scary for vue, so the
body
tag is out of bounds. in fact, as of version 2, if you attempt to mount there you will get this warning:
xxxxxxxxxx
"do not mount vue to <html> or <body> - mount to normal elements instead."
managing the
head
and
body
tags
so now that we've established that vue must mount on its very own node below the
body
, and it can't affect any part of the dom above this mount node, how do you manage the
body
or
head
with vue?
the answer is: you can't. well not directly, at least. anything outside the mount element is effectively invisible to vue.
but there's more to vue than rendering. so even though there are elements beyond its reach, it can still assist you in reachng them in other ways via watchers and lifecycle hooks.
scenario #1: listening to key events
let's say you're creating a modal window with vue and you want the user to be able to close the window with the escape key.
vue gives you the
v-on
directive for listening to events, but unless you are focused on a form input, key events are dispatched from the
body
tag:
since the
body
is out of vue's jurisdiction, you won't be able to get vue to listen to this event. you'll have to set up your own event listener with the web api:
xxxxxxxxxx
var app = new vue({
el: '#app',
data: {
modalopen: false
}
});
document.addeventlistener('keyup', function(evt) {
if (evt.keycode === 27 && app.modalopen) {
app.modalopen = false;
}
});
how vue can help
vue can help via its
lifecycle hooks
. firstly, use the
created
hook to add the listener. this ensures that data properties you're referencing (i.e.
modalopen
) are being observed when the callback is fired.
secondly, use the destroyed hook to remove the listener when it's no longer needed to avoid memory leaks.
xxxxxxxxxx
new vue({
el: '#app',
data: {
modalopen: false
},
methods: {
escapekeylistener: function(evt) {
if (evt.keycode === 27 && this.modalopen) {
this.modalopen = false;
}
}
},
created: function() {
document.addeventlistener('keyup', this.escapekeylistener);
},
destroyed: function() {
document.removeeventlistener('keyup', this.escapekeylistener);
},
});
scenario #2: managing
body
classes
when a user opens your modal window, you want to completely disable the main window. to do this you can stack it behind a semi-transparent panel so it can't be clicked, and clip any overflow so it can't be scrolled.
to prevent scrolling, add a class to the body (let's call it
modal-open
) which makes the
overflow: hidden
.
xxxxxxxxxx
body.modal-open {
overflow: hidden;
}
obviously, we need to dynamically add and remove this class, as we'll still want to allow scrolling when the modal is closed. we'd normally use
v-bind:class
to do this job, but again, you can't bind to
body
attributes with vue, so we're going to have to use the web api again:
xxxxxxxxxx
// modal opens
document.body.classlist.add('modal-open');
// modal closes
document.body.classlist.remove('modal-closed');
how vue can help
vue adds reactive getters and setters to each data property so that when data value changes it knows to update the dom. vue allows you to write custom logic that hooks into reactive data changes via watchers .
vue will execute any watcher callbacks whenever the data value (in this case
modalopen
) changes. we'll utilize this callback to update to add or remove the
body
class:
xxxxxxxxxx
var app = new vue({
el: '#app',
data: {
modalopen: false
},
watch: {
modalopen: function(newval) {
var classname = 'modal-open';
if (newval) {
document.body.classlist.add(classname);
} else {
document.body.classlist.remove(classname);
}
}
}
});
Published at DZone with permission of Anthony Gore, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Which Is Better for IoT: Azure RTOS or FreeRTOS?
-
Integration Testing Tutorial: A Comprehensive Guide With Examples And Best Practices
-
8 Data Anonymization Techniques to Safeguard User PII Data
-
IntelliJ IDEA Switches to JetBrains YouTrack
Comments