how to prevent click when animation is in progress with VUE?
<button v-model="show" #click="show === 'showFirst'">click me first</button>
<button v-model="show" #click="show === 'showSecond'">click me second</button>
<transition :enter-active-class="'animated fadeIn'" :leave-active-class="'animated fadeOut'">
<div class="first" key="1" v-if="showFirst">First Div</div>
<div class="second" key="2" v-if="showSecond">Second Div</div>
</transition>
VUE
export default {
data: function() {
return: {
show: "showFirst"
}
}
}
After clicking on the first button, both will be deactivated until the animation ends. Is it possible either in this way or in another way?
Now when I click both buttons alternately, the animations interrupt each other.
You can use <transition></transition> JS hooks.(https://v2.vuejs.org/v2/guide/transitions.html#JavaScript-Hooks)
<transition #before-enter="disabled=true" #after-leave="disabled=false">
<div class="first" key="1" v-if="showFirst">First Div</div>
<div class="second" key="2" v-if="showSecond">Second Div</div>
</transition>
listen to transition end with vanilla js
const transition = document.querySelector('.transition');
transition.addEventListener('transitionend', () => {
console.log('Transition ended');
});
There are some things I don't really get in your code though. Like why the buttons have v-models. What are you trying to perform two-way data binding for? I think just having the buttons trigger events is enough.
And why your click events are comparing and not assigning.
You could use the disabled prop for the buttons too, so they're disabled respective to a boolean you're manipulating.
So probably it should be something like this:
<button #click="isActivated" :disabled="!isEnabled">click me first</button>
<button #click="isActivated" :disabled="!isEnabled">click me second</button>
<transition :enter-active-class="'animated fadeIn'" :leave-active-class="'animated fadeOut'">
<div class="first" key="1" v-if="show === 'showFirst'">First Div</div>
<div class="second" key="2" v-if="show === 'showSecond'">Second Div</div>
</transition>
Then depending on the timing of your animations, you can have a disabled function that you pass as a prop to the button element.
Like this
data() {
return {
show: 'showFirst',
isEnabled: true,
}
},
methods: {
isActivated(){
//first starts off as false
this.show === 'showFirst' ? this.show = 'showSecond' : this.show = 'showFirst'
this.disableButtons();
},
disableButtons(){
//disable the button then enable after the number of seconds of the animation
this.isEnabled = false
setTimeout(() => {
this.isEnabled = true
}, noOfSecondsOfAnimation)
}
}
Related
I am following this library for making slick.
This is my current layout
I want to disable the click methods for arrow-left and arrow-right button, while keeping the arrow here, so that it will not change slide or anything. How to do it?
Codesandbox:
https://codesandbox.io/s/naughty-matan-55c76?file=/src/components/HelloWorld.vue
You can add #click.stop to stop event propagation. See more about Event Modifiers.
<VueSlickCarousel>
...
<template #prevArrow>
<button class="arrow-btn">
<img
src="#/assets/images/common/arrow-right.svg"
alt="arrow-left"
#click.stop>
</button>
</template>
<template #nextArrow>
<button class="arrow-btn">
<img
src="#/assets/images/common/arrow-right.svg"
alt="arrow-left"
#click.stop>
</button>
</template>
</VueSlickCarousel>
Updates
To disable click event from button (not only from img). You can do it with css:
.arrow-btn {
pointer-events: none;
img {
pointer-events: all;
}
}
But why not we just add #click.stop to button instead of img?
The problem is here:
...
arrow = this.prevArrow ? (
this.prevArrow(option)[0]
) : (
<button type="button" data-role="none" style="display: block;">
Next
</button>
)
...
mergeVNodeData(arrow, 'on', {
click: () => {
if (clickable) {
this.$emit('arrowClicked', { message: this.type })
}
},
})
First it checks if you have passed the prevArrow slot, if so they will use your slot. If not, they will use the default button.
And either way, they will combine their default props/event handlers which include the 'click' event, meaning that your click will only be overridden.
I have 2 buttons in the card, like the below.
<div class = "card">
<button class ="play"> Play </button>
<button class ="pause"> PAuse </button>
</div>
When I press PLay button, Pause should be enabled. Play button should be disabled.
After Pressing Play,If I press Pause button, Play button should be Enabled and Pause button should be disabled.
The one thing, We cannot directly press the Pause button, without pressing the Play button.
How could I write a code to show the conditions Play and Pause in JS.
Could anyone please help?
Thanks
Well, here is the vanilla Javascript version.
Html:
<div class = "card">
<button class ="play"> Play </button>
<button class ="pause"> Pause </button>
</div>
Javascript:
const playButton = document.querySelector('.play');
const pauseButton = document.querySelector('.pause');
// By default make pause button disabled
pauseButton.disabled = true;
playButton.addEventListener('click', () => {
pauseButton.disabled = false;
playButton.disabled = true;
});
pauseButton.addEventListener('click', () => {
playButton.disabled = false;
pauseButton.disabled = true;
});
This will work.
Here is the codepen
Set by default disabled="true" for .pause button and continue with set click event for two buttons
$(".play").on("click", function(){
$(this).prop("disabled", true );
$(".pause").prop("disabled", false );
})
$(".pause").on("click", function(){
$(this).prop("disabled", true );
$(".play").prop("disabled", false );
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class = "card">
<button class ="play"> Play </button>
<button class ="pause" disabled="true"> PAuse </button>
</div>
If you don't have controll on your DOM and can't add disabled="true" for .pause, you can use $(".pause").prop("disabled", true ) before define events
$(".pause").prop("disabled", true );
$(".play").on("click", function(){
$(this).prop("disabled", true );
$(".pause").prop("disabled", false );
})
$(".pause").on("click", function(){
$(this).prop("disabled", true );
$(".play").prop("disabled", false );
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class = "card">
<button class ="play"> Play </button>
<button class ="pause"> PAuse </button>
</div>
Consider the following.
$(function() {
$(".btn").click(function(e) {
if ($(this).hasClass("play")) {
$(this).toggleClass("play pause").html("⏸ Pause");
// Trigger Play event
} else {
$(this).toggleClass("play pause").html("⏵ Play");
// Trigger Pause Event
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="card">
<button class="btn play">⏵ Play</button>
</div>
This retains the one button yet changes the Class and Content of the button as needed.
Symbol Reference: http://www.scrollseek.com/wd/html_symbols_complete.html
<div class="gallery-container">
<?php while (have_rows('gallery')): ?>
[...]
<div class="toggle-container">
<button class="toggle-button active" onclick="gridView()">Grid</button>
<button class="toggle-button" onclick="listView()">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items...]
</div>
<?php endwhile; ?>
</div>
What would be the best way to select specific elements on a page when the elements are created with a while loop shown above. It's an ever-growing list and elements can also be removed.
In this example I am generating a page full of small galleries together with the toggle buttons for the Grid/List view next to each gallery.
I am trying to make all of those buttons work with just the gallery they are generated together with.
I know how to select them based on their index manually, but I don't know how I could tweak the code to be able to make it work with every small gallery separately.
This is what I came up with to make it work with the first gallery:
<script>
const button = document.getElementsByClassName('toggle-button');
const element = document.getElementsByClassName('gallery-items');
function listView() {
if ( element[0].classList.contains('grid-items') ){
element[0].classList.remove("grid-items");
}
button[0].classList.toggle('active');
button[1].classList.toggle('active');
}
function gridView() {
if ( !element[0].classList.contains('grid-items') ){
element[0].classList.add("grid-items");
}
button[0].classList.toggle('active');
button[1].classList.toggle('active');
}
</script>
You might consider using event delegation instead: add a click listener to .gallery-container. If the clicked target is a .toggle-button, run the appropriate logic, selecting the relevant surrounding elements on click:
document.querySelector('.gallery-container').addEventListener('click', ({ target }) => {
if (!target.matches('.toggle-button')) {
return;
}
const toggleContainer = target.parentElement;
const btns = toggleContainer.children;
if (target === btns[0]) {
btns[0].classList.add('active');
btns[1].classList.remove('active');
} else {
btns[0].classList.remove('active');
btns[1].classList.add('active');
}
const galleryItems = toggleContainer.nextElementSibling;
if (target === btns[0]) {
galleryItems.classList.add('grid-items');
} else {
galleryItems.classList.remove('grid-items');
}
});
.active {
background-color: yellow;
}
.grid-items {
background-color: red;
}
<div class="gallery-container">
<div class="toggle-container">
<button class="toggle-button active">Grid</button>
<button class="toggle-button">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items...]
</div>
<div class="toggle-container">
<button class="toggle-button active">Grid</button>
<button class="toggle-button">List</button>
</div>
<div class="gallery-items grid-items">
[...Gallery Items 2...]
</div>
</div>
Note that there's no need to explicitly test if a classList.contains a particular class before adding it (though, there's no harm in doing so, it's just unnecessary).
I'm creating a reusable checkbox component for vue and I've run into an interesting issue caused by vue's recycling of unused components.
This is best seen with an example:
Vue.component("checkbox", {
template: `
<div>
<slot></slot>:
<input type="checkbox" v-model="checked" v-on:change="updateVModel"/>
<span>changed!</span>
</div>
`,
props: {
value: {
type: Boolean,
required: true
}
},
data(){
return {checked: this.value};
},
watch: {
value(val){
this.changeAnimation();
this.checked = val;
}
},
methods: {
changeAnimation(){
let $span = this.$el.querySelector("span");
$span.classList.remove("animate");
setTimeout(() => $span.classList.add("animate"), 50);
},
updateVModel(){
this.changeAnimation();//this line is redundant since watch.value runs anyway
this.$emit("input", this.checked);
}
}
});
new Vue({
el: "#menu",
data: {
menu: 0,
checked0: true,
checked1: false
}
});
span{
transition: 1s;
opacity: 0;
}
span.animate{
animation: notice-me .3s ease-in-out 4 alternate
}
#keyframes notice-me{
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="menu">
<div v-if="menu == 0">
<button v-on:click="menu = 1">Go to menu 1</button>
<h3>Menu 0</h3>
<checkbox v-model="checked0">checked0</checkbox>
<checkbox v-model="checked0">checked0</checkbox>
</div>
<div v-else>
<button v-on:click="menu = 0">Go to menu 0</button>
<h3>Menu 1</h3>
<checkbox v-model="checked1">checked1</checkbox>
<checkbox v-model="checked1">checked1</checkbox>
</div>
</div>
I've made my components as so whenever the value changes, either from a user click(with v-on:click) or from the variable itself changing (with watch.value), the word "changed!" blinks besides the checkbox a couple of times. Everything works fine, but the problem arises when we change menus using the "Change to menu" button and checked0 and checked1 are different values. "changed" will blink even though it shouldn't have.
This is obviously caused by vue recycling the components and using them for another variable. Since the variable's value is different from the old one, watch.value is run, triggering the animation when we'd expect it not to happen.
I did a little bit of research and found that I could give all my different checkboxes vue keys like so: <checkbox v-model="checked1" key="thing1">checked1</checkbox>, but I want to fix this elegantly and allow vue to recycle whatever it wants. There should be a way to detect if the value changed because it actually did or because of recycling.
So my question is how to I fix this problem, or how do I write my code differently to avoid it?
A key should really be associated with the conditionally-rendered (by v-for or v-if) unit. It should be bound to whatever is unique about the unit. In your case, you can use menu:
<div v-if="menu == 0" :key="menu">
<button v-on:click="menu = 1">Go to menu 1</button>
<h3>Menu 0</h3>
<checkbox v-model="checked0">checked0</checkbox>
<checkbox v-model="checked0">checked0</checkbox>
</div>
<div v-else :key="menu">
<button v-on:click="menu = 0">Go to menu 0</button>
<h3>Menu 1</h3>
<checkbox v-model="checked1">checked1</checkbox>
<checkbox v-model="checked1">checked1</checkbox>
</div>
I have 2 button one will open Dialog box and make some changes with its <div> and the will not open any dialog box, it will just make some styling changes within its <DIV>
I will have multiple instances of the <DIV>s which contains these 2 buttons these <DIV>s will be generating at runtime so there can be "N" number of buttons.
I have created the FIDDLE to give a demo of my code: http://jsfiddle.net/aasthatuteja/ZtLEq/
Now, for the button which "DOES NOT" generate Dialog Box, I am able to make changes using jquery by catching its ".closest ('parent div')" and then on click of this particular button, it make changes for its parent <DIV> only.
BUt the ISSUE IS: with the button which generates Dialog Box, I am not sure on click of which button the dialog box have been generated, so on submit I am not sure which particular parent <Div> should be impacted.
Below is my code:
HTML
<div id="content-1" class="content">
<div class="rx-container"> <span title="Accept" class="Accepted">Accept</span>
<br>
<br> <span title="Reject" class="Rejected">Reject</span>
<div class="rx-statusMessage">
<br>
<br>
<p class="rx-statusAccepted nodisplay">Accepted</p>
<p class="rx-statusRejected nodisplay">Rejected</p>
<div class="rejectReasonBox nodisplay">Reason: <span>Incorrect Label Applied</span>
</div>
</div>
</div>
</div>
<div id="content-2" class="content">
<div class="rx-container"> <span title="Accept" class="Accepted">Accept</span>
<br>
<br> <span title="Reject" class="Rejected">Reject</span>
<div class="rx-statusMessage">
<br>
<br>
<p class="rx-statusAccepted nodisplay">Accepted</p>
<p class="rx-statusRejected nodisplay">Rejected</p>
<div class="rejectReasonBox nodisplay">Reason: <span>Incorrect Label Applied</span>
</div>
</div>
</div>
</div>
<div id="content-3" class="content">
<div class="rx-container"> <span title="Accept" class="Accepted">Accept</span>
<br>
<br> <span title="Reject" class="Rejected">Reject</span>
<div class="rx-statusMessage">
<br>
<br>
<p class="rx-statusAccepted nodisplay">Accepted</p>
<p class="rx-statusRejected nodisplay">Rejected</p>
<div class="rejectReasonBox nodisplay">Reason: <span>Incorrect Label Applied</span>
</div>
</div>
</div>
</div>
<div id="rejectReason" title="Reason">
<p>Please provide the reason for cancelling the session.</p>
<div class="inputRow">
<textarea id="rejectReasonBox" class="reasonBox">Incorrect Label Applied</textarea>
</div>
</div>
JQUERY
$(document).ready(function () {
function aceeptMethod() {
var $parent = $(this).closest('.rx-container');
$('.rx-statusRejected', $parent).hide();
$('.rx-statusAccepted', $parent).show();
$('.rejectReasonBox', $parent).hide();
$('.k-tabstrip-items .k-state-default .k-link', $parent).css('color', '#7ea700');
$('.k-tabstrip .k-state-active', $parent).css('border-color', '#7ea700');
$('.k-tabstrip-items .k-state-active, .k-tabstrip .k-content.k-state-active', $parent).css('background-color', '#f5f5e9');
$(this).attr("title", "Accepted");
$(this).next().attr("title", "Reject");
$(this).removeClass('Accepted');
$(this).addClass('disableAccepted');
$(this).next().removeClass('disableRejected');
$(this).next().addClass('Rejected');
checkIfAccepted();
}
function rejectMethod() {
$('.k-tabstrip-items .k-state-default .k-link').css('color', '#ff0000');
$('.k-tabstrip .k-state-active').css('border-color', '#ff0000');
$('.k-tabstrip-items .k-state-active,#content-1 .k-tabstrip .k-content.k-state-active').css('background-color', '#f5e9e9');
$('.rx-statusRejected').show();
$(".rx-statusAccepted").hide();
$(".rejectReasonBox").show();
$('.Rejected').attr("title", "Rejected");
$('.Rejected').prev().attr("title", "Accept");
$('.Rejected').addClass('disableRejected');
$('.disableRejected').prev().addClass('Accepted');
$('.disableRejected').prev().removeClass('disableAccepted');
$('.disableRejected').removeClass('Rejected');
checkIfRejected();
}
$("#rejectReason").dialog({
autoOpen: false,
modal: true,
buttons: {
"Submit": function () {
$(this).dialog("close");
rejectMethod();
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
$(".Accepted").click(aceeptMethod);
$(".Rejected").click(function () {
$("#rejectReason").dialog("open");
});
function checkIfAccepted() {
if ($(".rx-statusAccepted:visible").length == $(".rx-statusAccepted").length) {
$('#authorizeOrderButton').prop('disabled', false);
$('#authorizeOrderButton').removeAttr("disabled");
$('#authorizeOrderButton').removeClass("greyButton");
}
}
function checkIfRejected() {
if ($(".rx-statusAccepted:visible").length <= $(".rx-statusAccepted").length) {
$('#authorizeOrderButton').attr("disabled", "disabled");
$('#authorizeOrderButton').addClass("greyButton");
}
}
});
Please suggest!
Let me know if you need some more informatuion or if the situation is not clear.
Thanks!
Probably the easiest way is to take advantage of closures and the click event's target parameter.
Instead of defining the dialog and calling "open" on it later, you should call it when you actually need the dialog box to open within the click event like so:
$(".accept").click(function(e){
$("#rejectReason").dialog({
autoOpen: true,
modal: true,
buttons: {
"Submit": function () {
$(this).dialog("close");
myUi._rejectMethod(e.target);
},
"Cancel": function () {
$(this).dialog("close");
}
}
});
});
This allows you to pass the button that was click through so you know which one you are working with.
Fiddler Example
As per "charlietfl Nov 15 at 23:06" suggestion I was able to fix my issue:
"here's a good start....what you have is far far too complicated. I only got some basics working to have proper rejection shown jsfiddle.net/ZtLEq/6 – charlietfl Nov 15 at 23:06"