Fade in and fade out transition using onClick - javascript

I'm trying to appear a background color with transition when a click event happens and without further interaction fade out the background color after 1 or 2 seconds.
Basically i want to do what active property does in css but for a click event.
My current approach needs to fire the click event for the second time to fadeout the background color. How can i do this in one click
My approach
handleClick(id) {
this.setState({
active: !this.state.active
})
}
<div className={this.state.active ? "txt_vote_bar_div txt_vote_bar_div_active" : "txt_vote_bar_div txt_vote_bar_div_notactive"}
onClick={this.handleClick()}>
</div>
My CSS
.txt_vote_bar_div {
width: 100%;
height: 50px;
min-height: 50px;
position: relative;
border: 1px solid #C6C6C6;
border-radius: 5px;
margin-bottom: 10px;
cursor: pointer;
}
.txt_vote_bar_div_active {
background-color: #001f3f;
transition: 1s ease-in-out;
}
.txt_vote_bar_div_notactive {
background-color: #FFFFFF;
transition: 1s ease-in-out;
}

Please find running example
In your code one mistake, you did i.e onClick={this.handleClick()}
so due to this after every renders your click event is triggered.
This is What I did.
class Hello extends React.Component {
state = {
active: false
}
handleClick(e) {
this.setState({
active: !this.state.active
});
setTimeout(() => {
this.setState({
active: !this.state.active
});
}, 1000);
}
render() {
return (
<div className = { this.state.active ? "txt_vote_bar_div txt_vote_bar_div_active" : "txt_vote_bar_div txt_vote_bar_div_notactive" } onClick = { this.handleClick.bind(this) }></div>
);
}
}
ReactDOM.render( <
Hello initialName = "World" / > ,
document.getElementById('container')
);

You could do this by setting a timeout to change it back.
handleClick(id) {
this.setState({
active: !this.state.active
});
// The timeout will trigger after 1000ms. Use a fat arrow function
// to keep the same reference to this.
setTimeout(() => {
this.setState({
active: false
});
}, 1000);
}
If you can't use fat arrow function you could assign this to a variable like self and call self.setState from within the timeout handler.

Related

ReactJS: Why is my custom functional toast component behaving so strangely upon attempting to automatically dismiss notification pop-ups?

My custom react toast component was working well until I tried to implement automatic dismissal of notifications after a set time.
I am trying to make it so that after a set time, the pop-up "toast" notifications will have a CSS fade-out animation play and then be deleted — unless the user is hovering over that notification, in which case it will hold off on dismissing that one until the user moves their mouse off of it.
Sometimes it works properly, other times it stops displaying anything and adds the notifications back one by one, other times it... well, it just behaves in a very strange and unexpected manner.
Here is my code:
Toast.css
.toast-container {
font-size: 24px;
box-sizing: border-box;
position: fixed;
z-index: 10;
}
.toast-popup {
padding: 12px;
display: flex;
align-items: center;
justify-content: space-between;
width: 500px;
border: solid #f2f2f2;
border-radius: 8px;
box-shadow: 0 0 10px #999;
margin-bottom: 1rem;
opacity: 0.9;
}
.toast-popup:hover {
box-shadow: 0 0 12px deepskyblue;
opacity: 1 !important;
animation-play-state: paused;
}
.success {
background-color: #5cb85c;
}
.info {
background-color: #5bc0de;
}
.warning {
background-color: #f0ad4e;
}
.danger {
background-color: #d9534f;
}
.toast-text {
justify-self: flex-start;
width: 100%;
padding: 6px 0 6px 6px;
opacity: 0.9;
}
.toast-title {
font-weight: 700;
font-size: 32px;
text-align: left;
padding-bottom: 0px;
color: #f2f2f2;
}
.toast-message {
padding-top: 0px;
text-align: left;
color: #f2f2f2;
}
.toast-icon {
float: left;
margin: 0 20px 0 10px;
opacity: 0.9;
}
.toast-icon img {
width: 50px;
height: 50px;
fill: #f2f2f2;
opacity: 0.9;
}
.close-button {
float: right;
align-self: flex-start;
font-weight: 600;
color: #f2f2f2;
background: none;
border: none;
opacity: 0.9;
cursor: pointer;
}
.top-right {
top: 2rem;
right: 2rem;
}
.top-right-slide {
top: 2rem;
right: 2rem;
transition: transform .6s ease-in-out;
animation: toast-in-right .7s;
}
.bottom-right {
bottom: 2rem;
right: 2rem;
}
.bottom-right-slide {
bottom: 2rem;
right: 2rem;
transition: transform .6s ease-in-out;
animation: toast-in-right .7s;
}
.top-left {
top: 2rem;
left: 2rem;
}
.top-left-slide {
top: 2rem;
left: 2rem;
transition: transform .6s ease-in;
animation: toast-in-left .7s;
}
.bottom-left {
bottom: 2rem;
left: 2rem;
}
.bottom-left-slide {
bottom: 2rem;
left: 2rem;
transition: transform .6s ease-in;
animation: toast-in-left .7s;
}
.fadeout {
animation: 4s linear 5s 1 normal forwards running toast-fadeout;
}
#keyframes toast-in-right {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
#keyframes toast-in-left {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
#keyframes toast-fadeout {
from { opacity: 0.9; }
to { opacity: 0; }
}
Toast.js - Please excuse the generous peppering of console.logs...
import React, {useEffect, useState} from 'react';
import icon_success from './icons/feathericons/check-circle.svg';
import icon_info from './icons/feathericons/info.svg';
import icon_warning from './icons/feathericons/alert-triangle.svg';
import icon_danger from './icons/feathericons/alert-octagon.svg';
import './Toast.css';
const Toast = (props) => {
const {toastList, position} = props;
const [list, setList] = useState(toastList);
const [prevId, setPrevId] = useState(0);
// This useEffect updates the list of toasts to display
useEffect(() => {
console.log('useEffect()');
console.log('useEffect() toastList:');
console.log(toastList);
setList([...toastList]);
}, [toastList]);
const markForDeletion = (toast) => {
if( toast.isDeleting ) {
return;
}
console.log(`toast ${toast.id} marked for deletion`)
toast.isDeleting = true;
setTimeout(() => {attemptDeletion(toast)}, 5000);
}
const attemptDeletion = (toast) => {
console.log(`attempting to delete toast ${toast.id}. canDelete = ${toast.canDelete}`);
if( toast.canDelete ) {
deleteToast(toast.id);
}
else {
console.log(`cannot delete toast ${toast.id}. `);
}
}
const getIcon = (variant) => {
switch( variant ) {
case 'success':
return icon_success;
break;
case 'info':
return icon_info;
break;
case 'warning':
return icon_warning;
break;
case 'danger':
return icon_danger;
break;
}
}
const generateId = (toast) => {
if( typeof(toast.id) === 'number' ) {
return toast.id;
}
toast.id = prevId + 1;
setPrevId(toast.id);
return toast.id;
}
const deleteToast = (id) => {
console.log(`deleting toast ${id}`);
const deletionIdxList = list.findIndex(e => e.id === id);
const deletionIdxToastList = toastList.findIndex(e => e.id === id);
console.log(`deletionIdxToastList: ${deletionIdxToastList}`);
if(deletionIdxList == null || deletionIdxList === -1) {
console.log(`cannot find list idx of id ${id}`);
console.log('list:');
console.log(list);
return;
}
if(deletionIdxToastList == null || deletionIdxToastList === -1) {
console.log(`cannot find toastList idx of id ${id}`);
console.log('toastList:');
console.log(toastList);
return;
}
console.log('list before deletion:');
console.log(list);
console.log('toastList before deletion:');
console.log(toastList);
console.log('list[deletionIdxList]:');
console.log(list[deletionIdxList]);
list.splice(deletionIdxList, 1);
console.log('toastList[deletionIdxToastList]:');
console.log(toastList[deletionIdxToastList]);
toastList.splice(deletionIdxToastList, 1);
setList([...list]);
console.log(`toast ${id} deleted successfully`);
console.log('list after deletion:');
console.log(list);
console.log('toastList after deletion:');
console.log(toastList);
}
return (
<>
<div className={`toast-container ${position}`} >
{
list.map((toast, i) => (
<div
key={i}
className={`toast-popup ${toast.variant} ${toast.isDeleting ? (position + ' fadeout') : (position + '-slide')}`}
onLoad={() => {
if( !toast.isLoaded ) {
toast.Id = generateId(toast);
toast.canDelete = true;
toast.isDeleting = false;
toast.isLoaded = true;
console.log(`on load ${toast.id}`);
setTimeout(() => markForDeletion(toast), 500);
}
}}
onMouseOver={() => {
toast.canDelete === true ? toast.canDelete = false : null;
toast.isDeleting === true ? toast.isDeleting = false : null;
console.log(`mouse over ${toast.id}`);
}}
onMouseLeave={() => {
toast.canDelete === false ? toast.canDelete = true : null;
markForDeletion(toast);
console.log(`mouse leave ${toast.id}`);
}}
>
<div className={'toast-icon'}>
<img src={getIcon(toast.variant)} />
</div>
<div className={'toast-text'}>
<div className={'toast-title'}>
{toast.variant.charAt(0).toUpperCase() + toast.variant.slice(1)}
</div>
<div className={'toast-message'}>{toast.message}</div>
</div>
<button
className={'close-button'}
onClick={() => {
toast.canDelete = true;
deleteToast(toast.id)
}
}>
X
</button>
</div>
))
}
</div>
</>
)
}
Toast.defaultProps = {
position: 'bottom-right'
}
export default Toast;
Snippet of Home.js where I am testing this new Toast component - A class component as I'm working on updating a pre-existing application to remove dependency on the react-toastify library
// Leaving out constructor and other irrelevant code...
toastSuccess() {
const newToast = {
variant: 'success',
message: 'This is a test of the success variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastInfo() {
const newToast = {
variant: 'info',
message: 'This is a test of the info variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastWarning() {
const newToast = {
variant: 'warning',
message: 'This is a test of the warning variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
toastDanger() {
const newToast = {
variant: 'danger',
message: 'This is a test of the danger variant toast pop-up.'
}
this.setState({
toastList: [...this.state.toastList, newToast]
});
}
render() {
return (
<div className="Home" style={{height:'100%'}}>
<Toast
toastList={this.state.toastList}
position={'bottom-right'}
/>
<div style={{display:'flex', justifyContent:'center'}}>
<Button onClick={() => this.toastSuccess()}>Success</Button>
<Button onClick={() => this.toastInfo()}>Info</Button>
<Button onClick={() => this.toastWarning()}>Warning</Button>
<Button onClick={() => this.toastDanger()}>Danger</Button>
</div>
{// ...}
</div>
);
}
Let me know if there's a way to get this code running here on StackOverflow using that Code Snippet feature, as that would be really helpful so that you readers can see the issue first-hand. Unfortunately I've never had any luck getting it to work, but I'll keep trying for a bit to see if I can figure it out.
EDIT:
Thanks to #Cristian-FlorinCalina for recommending StackBlitz as a good shareable test environment. I've got it set up there now, here's a link:
https://react-ts-ybunlg.stackblitz.io
First problem that I see with your code is that you are keeping two sources of truth for toast list. One is passed from the parent via props, and one is the internal state list in the Toast component. This is an antipattern that can generate a lot of issues.
Second BIG issue is that you are altering the list that you receive from the parent. That is a huge antipattern in React since props are readonly -- All React components must act like pure functions with respect to their props. (since you are altering an object inside an array apparently it works for the load update but it does not work when you are trying to call splice on the list -- this is why even if you deleted the element and applied the deletion effect, when it gets updated on the parent (next render) -> it will come back without it being removed and clicking again on another toast generate button will show you the previously deleted toast as well).
I think the big problem here is that you are not using composition properly. Instead of passing the toast list to the Toast component, you should keep the list on the parent, move the map from the child inside the parent. You will have one instance of Toast component per each element in the list.
Maybe you can have a ToastList component as well, that handles Toast compoonents based on their position... So when you click on Upper Left Toast Generator for example, it will add a new entry inside an array of toasts, with a position key. That array will be sent to the ToastList component, which will generate Toast components that handle their state internally (deletion, etc) and do not update the actual list. You can pass a function to the Toast component called onDelete that will be called by the Toast component on deletion, and you will update the ToastList state based on those events (probably propagate the delete event to the parent to update the list there).
Hope it makes sense.

drop shadow not showing when scrolled in react

I'm new in javascript react. I'm trying to implement when user scrolled down, the navbar would drop a shadow. but unfortunately it doesn't work at all, what did I do wrong??
I think there's a problem with my logic, or perhaps I do the whole code wrongly, if there's an problem with the code, please do tell me where did I do it wrong, thank you
The Code :
class Navbar extends Component {
constructor() {
super(); // super allows you to access parent class's methods and allows us to use "this." in constructor().
this.state = {
clicked: false,
scrolled: false,
}
// Note here too these bindings are necessary to make `this` work in the callback
// In general, we use binding whenever we use "setState" when handling an event
this.handleScroll = this.handleScroll.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleScroll = () => {
const offset = window.scrollY;
if (offset > 200) {
this.setState({scrolled : !this.state.scrolled})
}
}
handleClick = () => {
this.setState({ clicked: !this.state.clicked})
}
render() {
return(
<div className='mycontainer' onScroll={this.handleScroll}>
{/* Note here this.scroll.scrolled changes to this.state.scrolled */}
<nav className={this.state.scrolled ? "NavbarItems Scroll" : "NavbarItems"}>
<h1 className="navbar-logo">React <i className="fab fa-react"></i></h1>
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? "fas fa-times" : 'fas fa-bars'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{
MenuItems.map((items, index) => {
return (
<li key = {index}><a className={items.cName} href={items.url}>
{items.title}
</a></li>
);
})
}
</ul>
<p>{window.scrollY}</p>
</nav>
</div>
);
}
}
css :
.mycontainer {
top: 0;
left: 0;
width: 100%;
height: 200%;
}
.NavbarItems {
position: fixed;
width: 100vw;
height: 85px;
background-color: white;
transition: 0.2s;
display: flex;
align-items: center;
font-size: 1.2rem;
justify-content: center;
}
.Scroll {
box-shadow: 0px 1px 10px #999;
}
you need to subscribe to window.onScroll not for div className='mycontainer'
reed this topic
Update style of a component onScroll in React.js
and (it not related to your problem)
you shouldn't use
this.handleScroll = this.handleScroll.bind(this);
this.handleClick = this.handleClick.bind(this);
if you use class properties
handleScroll = () => {
...
}
handleClick = () => {
...
}
read this doc
https://reactjs.org/docs/faq-functions.html#how-do-i-bind-a-function-to-a-component-instance

React custom dropdown with event listener

I created a Dropdown that when I click outside of it the dropdown disappears. I used a click event listener to determine if I clicked outside the dropdown.
After a few clicks, the page slows down and crashes. Perhaps the state is being rendered in a loop or too many events are being fired at once?
How do I fix this?
Also, is there a more React way to determine if I clicked outside an element? (Instead of using a document.body event listener)
Here is the codepen:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
render() {
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
//console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
//console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
//console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);
#container {
position: relative;
height: 250px;
border: 1px solid black;
}
#dropdownHeader {
width: 100%;
max-width: 12em;
padding: 0.2em 0 0.2em 0.2em;
margin: 1em;
cursor: pointer;
box-shadow: 0 1px 4px 3px rgba(34, 36, 38, 0.15);
}
#dropdownContent {
display: flex;
flex-direction: column;
position: absolute;
top: 3em;
width: 100%;
max-width: 12em;
margin-left: 1em;
box-shadow: 0 1px 4px 0 rgba(34, 36, 38, 0.15);
padding: 0.2em;
}
#item {
font-size: 12px;
font-weight: 500;
padding: 0.75em 1em 0.75em 2em;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root">
<!-- This element's contents will be replaced with your component. -->
</div>
There's a pretty simple explanation for what you're experiencing. :)
The way I was able to figure it out was the number of warnings that were showing up in the terminal every time I clicked somewhere was getting higher and higher, especially when the state changed.
The answer though is that since you were adding the event listener code in the render function, every time the code re-rendered it would add more and more event listeners slowing down your code.
Basically the solution is that you should move the adding of event listeners to componentDidMount so it's only run once.
Updated working javascript:
const items = [
{
value: 'User1'
},
{
value: 'User2'
},
{
value: 'User3'
},
{
value: 'User4'
},
{
value: 'User5'
}
];
class Dropdown extends React.Component {
state = {
isActive: false,
}
// added component did mount here
componentDidMount(){
const { isActive } = this.state;
document.addEventListener('click', (evt) => {
if (evt.target.closest('#dropdownContent')) {
console.warn('clicked inside target do nothing');
return;
}
if (evt.target.closest('#dropdownHeader')) {
console.warn('clicked the header toggle');
this.setState({isActive: !isActive});
}
console.warn('clicked outside target');
if (isActive) {
this.setState({isActive: false});
}
});
}
render() {
const { isActive } = this.state;
//removed event listener here
return (
<div id="container">
<div id="dropdownHeader">select option</div>
{isActive && (
<div id="dropdownContent">
{items.map((item) => (
<div id="item" key={item.value}>
{item.value}
</div>
))}
</div>
)}
</div>
);
};
}
ReactDOM.render(
<Dropdown items={items} />,
document.getElementById('root')
);

Blink an image on scroll

My purpose is to make an image blink 3 times on a scroll (like lights on, then off, 3 times in a row with a 1 sec delay), then stay on until user scrolls down more that 3600px.
I've added event listener:
created() {
window.addEventListener('scroll', this.scrollAnimation)
}
On scroll i fire method scrollAnimation:
methods: {
scrollAnimation() {
let currentPos = window.pageYOffset
if (currentPos > 3000 && currentPos < 3600) {
this.$refs.supportOff.style.display = 'none'
this.$refs.supportOn.style.display = 'block'
} else {
this.$refs.supportOff.style.display = 'block'
this.$refs.supportOn.style.display = 'none'
}
}
}
And here's the template for the images:
<div class="support__image-wrapper">
<img ref="supportOff" class="support__image support__image_on" src="../../assets/images/247-off.png">
<img ref="supportOn" class="support__image support__image_off" src="../../assets/images/247-on.png">
</div>
Now this code works, when i scroll 3000 pixels down, but not lower than 3600 pixels, it shows 247-on image and hides 247-off image. But, there's an issues with blinking it, if i will use setInterval it's going to be fired every time a user scrolls between 3000 and 3600 px. What's the best way to achieve that blinking?
A few things to try...
Don't start manipulating the dom with $ref
Instead, create a variable which will trigger changes in the dom
methods: {
scrollAnimation() {
this.showSupport = window.pageYOffset > 3000 && window.pageYOffset < 3600
}
}
<div>
<img v-if="showSupport" class="blink" src="../../assets/images/247-on.png">
<img v-else src="../../assets/images/247-off.png">
</div>
Blinking I would advise using css animations (#keyframes). This way you can control the timing of the blink without anything in the script. It would just start blinking as soon as it's visible on the page.
.blink {
animation: blink 1s infinite;
}
#keyframes blink {
0% {opacity: 0}
49%{opacity: 0}
50% {opacity: 1}
}
Hope this helps.
Just wanted to add a quick demo for future readers, based on t3__rry's comment on how scroll based events which are not optimized/debounced can lead to serious performance issue; as well as Mulhoon's nice advice on utilizing CSS #keyframes for blinking animation:
new Vue({
el: '#app',
data() {
return {
blinkRate: 1000,
blinkCount: 3,
blinking: false,
blinkTimeoutId: -1,
state: false,
currentPos: window.pageYOffset
}
},
mounted() {
window.addEventListener('scroll', _.debounce(() => {
this.currentPos = window.pageYOffset;
if (this.currentPos > 3000 && this.currentPos < 3600) {
this.state = true;
}
else {
this.state = false;
}
}), 100);
},
methods: {
blink() {
if (this.blinkTimeoutId > -1) {
clearTimeout(this.blinkTimeoutId);
}
this.blinking = true;
this.blinkTimeoutId = setTimeout(() => {
this.blinking = false;
}, 1000 * this.blinkCount);
}
},
watch: {
state() {
this.blink();
}
}
});
#app {
background-color: gainsboro;
height: 2000vh;
padding: 10px;
}
.page-offset {
position: fixed;
top: 20px;
right: 20px;
}
.blinker > div {
border-radius: 50%;
border: 2px solid white;
color: white;
font-weight: bold;
height: 35px;
left: 20px;
line-height: 35px;
padding: 5px;
position: fixed;
text-align: center;
top: 20px;
vertical-align: middle;
width: 35px;
}
.blinker.animate > div {
animation: blink 1s infinite;
}
.blinker .on {
background-color: green;
}
.blinker .off {
background-color: crimson;
}
#keyframes blink {
0% {
opacity: 0
}
49% {
opacity: 0
}
50% {
opacity: 1
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<div id="app">
<div :class="['blinker', { 'animate': blinking } ]">
<div class="on" v-if="state">ON</div>
<div class="off" v-else>OFF</div>
</div>
<code class="page-offset">pageYOffset: {{Math.floor(currentPos)}}</code>
</div>

How to toggle a selected class in a global click event?

I'm creating my component library in vue, and I defined my component checkbox, the code is like this:
<template>
<div class="checkboxcont" :class="{'checkboxcont-selected': isSelected}" #click="clickevent">
<span class="j-checkbox">
<input type="checkbox" />
</span>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
isSelected: false
}
},
methods: {
clickevent(event) {
if(this.isSelected) {
this.isSelected = false;
} else {
this.isSelected = true;
}
}
},
}
</script>
Now, I hope that when I click the checkbox to set the data "isSelected" false, I can give the component class "checkboxcont-selected-last", and when I click other checkbox component, the classname "checkboxcont-selected-last" can be removed, how can I listen my click event to finish it? I try to use native JavaScript code to add the classname of the dom, but it seemed to have nothing when I binded the classname of my component with Vue.js:
clickevent(event) {
if(this.isSelected) {
this.isSelected = false;
this.$el.classList.add("checkboxcont-selected-last");
} else {
this.isSelected = true;
}
}
What should I do to solve this problem, please?
Here is my style code using less:
<style lang="less" scoped rel="stylesheet/less">
#import '../../mixin/mixin.less';
.checkboxcont {
display: inline-block;
&:hover {
cursor: pointer;
.j-checkbox {
border-color: #jbluelight;
}
}
}
.j-checkbox {
position: relative;
top: 0;
left: 0;
width: 12px;
height: 12px;
display: inline-block;
border: 1px solid #border;
border-radius: 3px;
line-height: 12px;
vertical-align: -3px;
margin: 0 5px;
z-index: 20;
transition: all .2s linear;
input {
opacity: 0;
position: absolute;
left: 0;
top: 0;
visibility: hidden;
/*display: none;*/
}
}
.checkboxcont-selected {
.j-checkbox {
background: #jbluelight;
border-color: #jbluelight;
&:after {
content: '';
width: 4px;
height: 7px;
border: 2px solid white;
border-top: none;
border-left: none;
position: absolute;
left: 3px;
top: 0;
z-index: 30;
transform: rotate(45deg) scale(1);
}
}
}
</style>
<style lang="less" rel="stylesheet/less">
#import '../../mixin/mixin.less';
.checkboxcont-selected-last .j-checkbox {
border-color: #jbluelight;
}
</style>
My initial thought is that I add the class by using this.$el after I clicked the component, it can be accessed because I dispatched the click event, and I just can't access the other component:
if(this.isSelected) {
this.isSelected = false;
this.$el.classList.add("checkboxcont-selected-last")
} else {
this.isSelected = true;
}
And I remove the class by using native HTML DOM operation when I dispatch the click event because I can not access the other component, so the complete definition of clickevent is that:
clickevent(event) {
let selectedLast = document.querySelector(".checkboxcont-selected-last");
if(selectedLast) {
selectedLast.classList.remove("checkboxcont-selected-last")
}
if(this.isSelected) {
this.isSelected = false;
this.$el.classList.add("checkboxcont-selected-last")
} else {
this.isSelected = true;
}
}
It looks good, but I can not add classname of my component when I use v-bind to bind my component's classname, is it wrong? And Is it unable to use native HTML DOM operation when I bind my component's classname with Vue?
A better way to dynamically add or remove class can be using v-bind:class. There are different ways you can add a dynamic class based on a vue data variable.
I see you are already using it:
<div class="checkboxcont" :class="{'checkboxcont-selected': isSelected}" #click="clickevent">
So here this div will have only one class : checkboxcont if isSelected is false, and two classes : checkboxcont and checkboxcont-selected if isSelected is true.
Edited:
Given that you want to add a class to DOM on another component, I can think of two ways:
Using Web API: You can do following if you know the id of the element you want to add class using Element.className:
var d = document.getElementById("yourElem") d.className += " otherclass"
Vuex way: You can have a centralised state provided by vue or use vuex to manage state, these state variables can be changed across components, and can be used to add/remove class dynamically.
You can have a look at my this answer to understand more about vuex.

Categories

Resources