N
Nuxtβ€’5d ago
deetstrab

Reactively add/remove a class name to <body>, from within a component...

I have a component with a boolean ref. As long as myBool.value === true, I'd like to bind a class name to <body>. I've attempted to use the useMeta and useMetaSafe composables (useHead > bodyAttrs.class) but it doesn't change reactively. Any idea what the heck I'm missing? This is driving me nuts and the documentation makes it sound relatively simple.
25 Replies
deetstrab
deetstrabβ€’5d ago
useHead works to set the initial value of title, bodyAttrs.class, etc -- but none of these properties appear to be reactive.
Cake
Cakeβ€’5d ago
how are u using useHead
Inès
Inès‒4d ago
You should use: useHead({ bodyAttrs: { class: 'YOUR-CLASS-NAME' } }) But indeed, as the doc says (https://nuxt.com/docs/api/composables/use-head), bodyAttrs is non-reactive...
xibman
xibmanβ€’4d ago
@deetstrab try with a composable for the class like useHead({ bodyAttrs: { class: () => test === true ? 'clas-true' : 'class-false' } })
deetstrab
deetstrabβ€’4d ago
It says it is reactive, though?
No description
deetstrab
deetstrabβ€’4d ago
This is exactly what I'm doing and have tried all variants of this that I know to.
useHead({
bodyAttrs: {
// class: displayMobileNav.value ? 'navOpen' : '' // Didn't work
class: () => displayMobileNav.value ? 'navOpen' : '' // Works like a charm inside the anonymous function
}
})
useHead({
bodyAttrs: {
// class: displayMobileNav.value ? 'navOpen' : '' // Didn't work
class: () => displayMobileNav.value ? 'navOpen' : '' // Works like a charm inside the anonymous function
}
})
I need to understand why this needs to be a function to work? What would be a good resource to read to better grasp this concept? Thanks in advance! πŸ™‚
Zampa
Zampaβ€’4d ago
Is there a reason it has to be body and not just a wrapper div in app.vue?
deetstrab
deetstrabβ€’4d ago
Thank you all for your helpfulness and thoughtful replies As I see it, yes. I needed to disable scrolling of the website while my mobile nav is open. It's possible a wrapper div would have worked too - this is a pattern I've used in many mobile navs that I've built over the years, though.
Zampa
Zampaβ€’4d ago
You could just have a global utility class:
body.noScroll {
overflow: hidden;
}
body.noScroll {
overflow: hidden;
}
and then you can just use a computed property or a watcher to apply/remove document.body.style.noScroll as needed?
Zampa
Zampaβ€’4d ago
I do something similar for locking the body when a modal is in place:
No description
deetstrab
deetstrabβ€’4d ago
Yes but isn't targeting the DOM nodes like this an anti-pattern in Vue/Nuxt? That's exactly how I do it in vanilla/es I just wanted to force myself to do it the Vue/Nuxt way
Zampa
Zampaβ€’4d ago
it's just adding/removing a class - it's what Vue is doing to the DOM underneath its hood, too.
deetstrab
deetstrabβ€’4d ago
well of course and I see it as that simple too, fwiw - I've just been taught that it's an antipattern. I didn't consider it for that reason, but you're absolutely right - it's a simple solution that works perfectly! I need to get a better understanding of why this only works within an anonymous function
Zampa
Zampaβ€’4d ago
Did you try something like:
useHead({
bodyAttrs: {
class: computed(() => {
if (isMobileNavVisible.value) return 'menu-is-open';

return '';
}),
},
});
useHead({
bodyAttrs: {
class: computed(() => {
if (isMobileNavVisible.value) return 'menu-is-open';

return '';
}),
},
});
deetstrab
deetstrabβ€’4d ago
no, I was (formerly) just using a ternary eval
Zampa
Zampaβ€’4d ago
the "computed" part of that would be necessary
deetstrab
deetstrabβ€’4d ago
I really appreciate your help and conversation πŸ™‚ thank you, my friend
Zampa
Zampaβ€’4d ago
πŸ‘
harlan
harlanβ€’4d ago
if you don't wrap with a function or computed the value gets resolved immediately, meaning you're providing a literal to useHead which it can't bind reactivity to alternatively you can avoid the function syntax if you provide the ref explicitely like so
useHead({
bodyAttrs: {
class: {
navOpen: displayMobileNav
}
}
})
useHead({
bodyAttrs: {
class: {
navOpen: displayMobileNav
}
}
})
this will work the same as the Vue template
FiveDigitLP
FiveDigitLPβ€’3d ago
I hate to piggy-back on this, but at the same time I am dealing with something very similar. The primary difference being that I don't even have any sort of conditional for the class. My goal is to have a body class that shows up on some pages and not on others. I thought it would be simple enough if I set bodyAttrs differently between pages, but that doesn't seem to be doing it. I have tried both setting the value bodyAttrs: { class: } to a ref and adding an anonymous function as above, but the class still seems to stick between pages. Any ideas?? Here's what I have for my pricing page:
const bodyClasses = ref('background-glow')

useHead({
title: pageTitle,
meta: [
{ name: 'description', content: pageDescription }
],
bodyAttrs: {
class: () => bodyClasses.value
}
})
const bodyClasses = ref('background-glow')

useHead({
title: pageTitle,
meta: [
{ name: 'description', content: pageDescription }
],
bodyAttrs: {
class: () => bodyClasses.value
}
})
And here's another page:
const bodyClasses = ref('')

useHead({
titleTemplate: null,
title: pageTitle,
meta: [
{ name: 'description', content: pageDescription }
],
bodyAttrs: {
class: () => bodyClasses.value
}
})
const bodyClasses = ref('')

useHead({
titleTemplate: null,
title: pageTitle,
meta: [
{ name: 'description', content: pageDescription }
],
bodyAttrs: {
class: () => bodyClasses.value
}
})
The title and page description changes, so I'm not sure why the bodyAttrs doesn't.
Cake
Cakeβ€’3d ago
try
useHead({
bodyAttrs:() => ({
class: bodyClasses.value
})
})
useHead({
bodyAttrs:() => ({
class: bodyClasses.value
})
})
FiveDigitLP
FiveDigitLPβ€’2d ago
Thanks! That didn't seem to work. At this point, I think it might have something to do with the way I have my page routes set up. For some reason, I have a software.vue page set up in the root of my pages folder in addition to having the nested routes under /software. I remember reading in the documentation during the early stages of my project about this being an option, but I don't remember why exactly I chose this structure as I think it has caused more headaches than it's worth.
manniL
manniLβ€’2d ago
this was fixed recently in unhead FYI
manniL
manniLβ€’2d ago
GitHub
Body style background image does not update when theme changes Β· Is...
Environment Operating System: Darwin Node Version: v21.6.2 Nuxt Version: 3.11.1 CLI Version: 3.11.1 Nitro Version: 2.9.5 Package Manager: yarn@1.22.21 Builder: - User Config: devtools, typescript, ...
FiveDigitLP
FiveDigitLPβ€’2d ago
Wait. You mean to tell me this is a bug and I'm not crazy?? πŸ˜