How to change the colour of a button when clicked on? (Vue) - vue.js

I'm trying to get a button that changes its color when pressed on it. When it is pressed again, it should change back to its original color. What am I doing wrong?
The Button in my template:
<th><Button v-bind:class="{'white': !clicked, 'blue': clicked}" v-on:click ="!clicked" ></Button></th>
<script>
export default {
data: {
clicked: false
}
}
</script>
<style>
.white {
background-color: white;
width: 200px;
height: 200px;
}
.blue {
width: 200px;
height: 200px;
background-color: blue;
}
</style>

You should set clicked property explicitly by #click="clicked = !clicked":
<th>
<Button
v-bind:class="{'white': !clicked, 'blue': clicked}"
v-on:click ="clicked = !clicked"
/>
</th>

Related

How can i change style of the body when my modal it will be open?

How can i change the body{overflow:hidden} when my modal it will be open?
for example it will be my modal, when its open, i would like to apply this style body{overflow:hidden}
<div v-if="dialogFoundation">
i am using vuejs3, i am using setup(){...}
The best performance would be to use javascript plain. You can add Eventlistener top the modal trigger Element. In my example i use a button. If it triggered then you can use classList and assign the body a class. In my example .dark.
Vue version
<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
<div id="app">
<h1>{{message}}</h1>
<p></p>
<button #click="doSomething">Modal</button>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Welcome to Vue!'
};
},
methods: {
doSomething() {
const b = document.querySelector('body');
b.classList.toggle('dark');
}
}
};
</script>
<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
a,
button {
color: #4fc08d;
}
button {
background: none;
border: solid 1px;
border-radius: 2em;
font: inherit;
padding: 0.75em 2em;
}
.dark {
background: black;
opacity: 0.4;
}
</style>
Vanilla JS
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
const b = document.querySelector('body');
b.classList.toggle('dark');
})
.dark {
background: black;
opacity: 0.4;
}
<body>
<div></div>
<button>click</button>
</body>
You can use watchers in Vue.js for solving this problem.
When variables changes you can check whether it is true or not, and if true change overflow of body to hidden.
{
watch: {
dialogFoundation(dialogFoundation) {
document.body.style.overflow = dialogFoundation ? "hidden" : "auto"
}
}
}
But I think this is not good solution. You can set this styles to your app element
#app {
width: 100%;
height: 100%;
overflow: auto;
}
and you can change style of app element using Vue directives.
<template>
<div id="app" :class="{ hidden: dialogFoundation }">
Long text....
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const dialogFoundation = ref(true);
return { dialogFoundation };
},
};
</script>
<style>
html,
body,
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app {
overflow: auto;
}
#app.hidden {
overflow: hidden;
}
</style>
Code in codesandbox - https://codesandbox.io/s/immutable-glitter-rwc2iy?file=/src/App.vue

Custom switch component with v-model doesn't work

trying to do my custom Switch component in VueJS following these manuals Using v-model on Components, Adding v-model Support to Custom Vue.js Components but something wrong and it doesn't work.
My SwitchTest component:
<template>
<div>
<input type="checkbox" :id="_uid"
:value="value"
#input="updateData"
class="checkbox">
<label :for="_uid" class="switch"></label>
</div>
</template>
<script>
export default {
props: ["value"],
data() {
return {};
},
methods: {
updateData($event) {
console.log($event.target.value);
this.$emit("input", $event.target.value);
}
}
};
</script>
<style scoped>
.switch {
position: relative;
display: inline-block;
width: 40px;
height: 20px;
background-color: rgba(0, 0, 0, 0.25);
border-radius: 20px;
transition: all 0.3s;
}
.switch::after {
content: "";
position: absolute;
width: 18px;
height: 18px;
border-radius: 50%;
background-color: white;
top: 1px;
left: 1px;
transition: all 0.3s;
}
.checkbox:checked + .switch::after {
left: 20px;
}
.checkbox:checked + .switch {
background-color: #7983ff;
}
.checkbox {
display: none;
}
</style>
And using SwitchTest component:
{{switch1}}
<SwitchTest v-model="switch1"/>
{{switch2}}
<SwitchTest v-model="switch2"/>
You can see the whole MVCE example here: https://codesandbox.io/s/ecstatic-meninsky-zx7w0?file=/src/App.vue:32-131
When I toggle checkbox, its value doesn't change.
Where am I wrong?
Thank you.
Checkboxes are a special case where they have two states (checked / unchecked) which can be translated to two values. Normally, this is simply true / false but since you've bound a single :value, you'll only ever get the same value each time.
Try binding the Boolean value prop to checked and emit a true / false value
<input
type="checkbox"
:checked="value"
:id="_uid"
#input="$emit('input', $event.target.checked)"
class="checkbox"
>

Button doesn't call the function for the first click

I have a checkbox and a button that when it's checked, then, the button suppose to submit, but when it's unchecked, the button is disable.
However, my button keep disabling and I can't submit, the reason because I add: v-on:click.prevent="checkboxJsInput"
The reason I use "prevent" is because I have disable and enable button, and "prevent" is to disable the button from submit when it's disable.
If I don't use "prevent", the button would submit even it's disable and the strange thing it's enable for the first time, but after you "check" for the second and so on, it's working.
My question, how to make the button works?
so when the button is grey, it's supposed to disable, when it's blue, it's supposed to enable to submit.
Is there a way to do this in vue way rather than using native javascript in vue?
checkboxJsInput: function() {
const checkBox = document.querySelector(".guestlistCheckboxJs");
const buttonFormJs = document.querySelector(".buttonCheckboxJs");
if (checkBox != null) {
checkBox.addEventListener("click", function() {
if (this.checked) {
buttonFormJs.disabled = false;
buttonFormJs.classList.remove("disabled");
} else {
buttonFormJs.disabled = true;
buttonFormJs.classList.add("disabled");
}
});
}
}
}
.buttonCheckboxJs.disabled {
background-color: #d6d6d6;
}
.guestlist-form-wrapper-checkbox {
display: flex;
margin: 20px 0 30px 0;
}
.guestlist-text-checkbox {
color: #ffffff;
margin: 5px 10px;
}
.guestlist-input-checkbox[type="checkbox"] {
display: none;
width: 45px;
height: 45px;
background: #d6d6d6;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05),
inset 0 -15px 10px -12px rgba(0, 0, 0, 0.05);
}
.guestlist-span-checkbox {
visibility: visible;
content: "";
display: block;
width: 25px;
height: 25px;
font-size: 20px;
text-align: center;
color: #ffffff;
background-color: #4990e2;
display: flex;
justify-content: center;
align-items: center;
border-radius: 3px;
}
.guestlist-input-checkbox[type="checkbox"]:checked
+ label
.guestlist-span-checkbox::before {
content: "✔";
}
.guestlist-form-wrapper-textarea {
width: 100%;
margin-top: 25px;
}
.guestlist-textarea {
background-color: transparent;
text-indent: 5px;
height: 50px;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
color: #ffffff;
}
.button-style {
background-color: #4990e2;
border: 0;
color: #ffffff;
font-weight: bold;
font-size: 18px;
padding: 15px 20px;
border-radius: 5px;
width: 600px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="guestlist-form-wrapper-checkbox">
<input
class="guestlist-input-checkbox guestlistCheckboxJs"
v-on:click="checkboxJsInput"
type="checkbox"
value="1"
name="OptIn"
id="OptIn"
>
<label class="guestlist-label-checkbox" for="OptIn">
<span class="guestlist-span-checkbox"></span>
</label>
</div>
<button
v-on:click.prevent="checkboxJsInput"
class="button-style buttonCheckboxJs disabled"
>Check Availability</button>
First of all I don't understand why you are using an event listner inside an event listner for that checkbox click.
Secondly, you can use v-model.
In your data, add property check like this:
data() {
return {
check: false
}
}
Then in your html use like this:
<div class="guestlist-form-wrapper-checkbox">
<input
class="guestlist-input-checkbox guestlistCheckboxJs"
v-model="check"
type="checkbox"
value="1"
name="OptIn"
id="OptIn"
>
<label class="guestlist-label-checkbox" for="OptIn">
<span class="guestlist-span-checkbox"></span>
</label>
</div>
<button
:disabled="check"
class="button-style buttonCheckboxJs disabled"
>Check Availability</button>
I just use a check property which is a boolean. Then you can run your function on click of that button.
Consider using v-model for this.
https://v2.vuejs.org/v2/guide/forms.html#v-model-with-Components
Your checkbox can have its v-model bound to a data property, and the disabled property on the button can be bound to the same. It'd look like this:
<template>
<input type="checkbox" v-model="enableSubmit" />
<button #click="mySubmitFunction()" :disabled="!enableSubmit">
</template>
<script>
export default {
data: function() {
return {
enableSubmit: false
}
},
methods: {
mySubmitFunction() {
//...
}
}
}
</script>

How to close a modal by clicking on the backdrop in Vue.js

I have created a simple reusable modal component using Vue.js and it works fine, but I want to make so that when I click on the backdrop the modal closes, how can I achieve this? I searched and found a similar question on stackoverflow:
vuejs hide modal when click off of it
And did the same that the accepted answer does, putting #click="$emit('close')" on the wrapper but the modal does not get closed by clicking the backdrop as it is in the provided example. Here is my code:
<template>
<div :class="backdrop" v-show="!showModal">
<div class="modal-wrapper">
<div class="modal-container" :class="size" #click="$emit('close')">
<span class="close-x" #click="closeModal">X</span>
<h1 class="label">{{label}}</h1>
<div class="modal-body">
<slot></slot>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'custom-modal',
data() {
return {
showModal: false
};
},
props: {
label: String | Number,
size: String,
backdrop: String
},
components: {
'custom-btn': customBtn
},
methods: {
closeModal() {
this.showModal = true;
}
}
};
</script>
<style>
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
.modal-container {
margin: 0px auto;
padding: 20px 30px;
border-radius: 2px;
background-color: #fff;
font-family: Helvetica, Arial, sans-serif;
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
transition: all .3s ease;
}
.close-x {
color: #00A6CE;
float: right;
}
.close-x:hover {
cursor: pointer;
}
</style>
Without a library you need to set it up like this:
<div class="modal-wrapper" #click="$emit('close')>
<div class="modal-container" :class="size" #click.stop=""></div>
</div>
It looks like you're missing the #click.stop="" which is required. Additionally you want to move the $emit('close') up to the modal-wrapper level.
With a library it may be overkill, but this is something that I have used v-click-outside for.
Vue directive to react on clicks outside an element without stopping the event propagation. Great for closing dialogues, menus among other things.
Simply npm install --save v-click-outside
Then (from the docs):
<div v-click-outside="onClickOutside"></div>
and:
onClickOutside (event, el) {
this.closeModal();
},
Try creating a transparent div that covers all the screen but with a z-index < your modals z-index. Then #click on it, you emit your event to close the modal :) Hope it will hellp
<template>
<div #click="handleBackdropClick" class="backdrop" ref="backdrop">
<div class="modal">
<h1> Modal Title </h1>
<input type="text" />
<p> Modal Content </p>
</div>
</div>
</template>
<style>
.modal {
width: 400px;
padding: 20px;
margin: 100px auto;
background: white;
border-radius: 10px;
}
.backdrop{
top: 0;
position: fixed;
background: rgba(0,0,0,0.5);
width: 100%;
height: 100%;
}
.close{
display: none;
}
</style>
export default {
methods: {
handleBackdropClick(e){
console.log(e)
if (e.path[0].className == "backdrop") {
this.$refs.backdrop.classList.add('close');
}
}
}
}
</script>

Vue I want an image to follow the mouse

I am having trouble with a Vue component https://jsfiddle.net/shawnswebsites/fep1p02c/20/. I have a div in the component and when the user's mouse enters the div I want an image to be shown and I want the image to follow the mouse.
new Vue({
el: '#app',
data: {
showImage: false,
page: {
left : 0,
top: 0
},
},
methods: {
onMouseMove(e) {
console.log('page x: ' + this.page.left);
this.page.left = e.pageX;
this.page.top = e.pageY;
}
}
})
.container {
height: 400px;
width: 400px;
border: 1px solid #FFF;
background-color: grey;
margin: 40px;
}
.image {
position: absolute;
z-index: 1000;
overflow:hidden;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container"
#mouseenter="showImage = true"
#mousemove.self="onMouseMove($event)"
#mouseleave="showImage = false">
</div>
<img v-show="showImage" class="image" src="http://via.placeholder.com/350x150" :style="{ left: page.left + 'px', top: page.top + 'px' }">
</div>
I use a #mouseenter to show the image and #mouseleave to hide the image. However, #mouseleave is still being called as I scroll over the div, which is causing the image to blink on and off. Can any help?
As said Oxcarga in the comment below you need to add pointer-events: none; to your image style:
.image {
position: absolute;
z-index: 1000;
overflow:hidden;
pointer-events: none;
}