I am trying to remove element from array if animation ends, but i get error: index is not defined.
How to correctly find specific index and remove it if animation ends?
They are drop() and remove() methods. drop() methods works well(i think) and elements are correctly added to the DOM.
Single file component looks like this:
<template>
<div class="card" :class="classObject">
<div class="card-image">
<figure class="image" #click="randomImage">
<img src="../../img/one.png" alt="Placeholder image" v-if="selected === 0">
<img src="../../img/two.jpg" alt="Placeholder image" v-else-if="selected === 1">
<img src="../../img/three.jpg" alt="Placeholder image" v-else>
</figure>
</div>
<div class="card-content has-text-centered">
<div class="content">
<div class="title is-size-1 has-text-weight-bold">
<span v-show="score >= 10">🎉</span>
{{score}}
<span v-show="score >= 10">🎉</span>
</div>
<div v-if="score >= 5" class="has-text-grey">
╮ (. ❛ ᴗ ❛.) ╭
</div>
<div v-else-if="score < 5 && score > 0" class="has-text-grey">
༼ つ ◕_◕ ༽つ
</div>
<div v-else class="has-text-grey">
(・_・ヾ
</div>
</div>
</div>
<footer class="card-footer">
<a class="card-footer-item" #click="score++">more 👍</a>
<a class="card-footer-item" #click="score--">less 👎</a>
<a class="card-footer-item" #click="drop" :disabled="score < 1">butt 💩</a>
</footer>
<transition-group name="drop" v-on:after-enter="remove(index)">
<img src="../../img/image.png" class="image" alt="an image" v-for="(item, index) in items" :key="index">
</transition-group>
</div>
</template>
<script>
export default {
data() {
return {
score: 23,
selected: 0,
images: [
'./img/one.png',
'./img/two.jpg',
'./img/three.jpg'
],
items: []
}
},
methods: {
debug(data) {
console.log(data);
},
randomImage() {
this.selected = Math.floor((Math.random() * 3))
},
drop() {
this.items.push(this.item);
},
remove(item) {
this.items.splice(item, 1);
}
},
computed: {
image() {
return this.selected;
},
classObject() {
return {
hard: this.score >= 42,
sixnine: this.score == 69
}
}
}
}
</script>
<style>
.image {
position: absolute;
top: calc(0vh - 500px);
left: 0;
right: 0;
/* pointer-events: none; */
/* top: 50%;
left: 50%;
transform: translate(-50%); */
}
.drop-enter-active {
transition: transform 3s;
}
.drop-enter {
transform: translateY(0vh);
}
.drop-enter-to {
transform: translateY(calc(100vh + 500px));
}
</style>
An Instance of finding the specific index and remove it from the array.
In below snippet, find the index which has { id: 2 } and removes it from an array.
const array = [{ id: 1 }, { id: 2 },
{ id: 3 }];
const index = array.findIndex((f) =>
{ return f.id && f.id === 2; });
console.log(index);
if(index > -1) {
// remove entry from found index
array.splice(index, 1);
console.log(array);
}
The issue comes when you are trying to call a function when a reference is needed. Instead of v-on:after-enter="remove(index)", try this v-on:after-enter="remove". So, when the v-on:after-enter is triggered its going to call the remove(item) using the reference you've already given to it. If you are giving a reference to a function you only use the name of that function.
You can find index of element in array with findIndex method:
remove(item) {
const index = this.items.findIndex(arrayItem => arrayItem === item);
this.items.splice(index, 1);
}
I modified the code to make it more obvious what I want to do
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css" integrity="sha256-vK3UTo/8wHbaUn+dTQD0X6dzidqc5l7gczvH+Bnowwk=" crossorigin="anonymous" />
<title>life is vuetiful</title>
</head>
<body class="has-background-primary">
<style>
html {
background-color: transparent;
}
body {
width: 42%;
margin: 2em auto;
}
a[disabled] {
color: grey;
cursor: default;
background-color: lightgray;
}
.hard {
border: 10px solid purple;
}
.sixnine {
background-color: pink;
border: 20px solid hotpink;
outline: 15px solid pink;
}
.image {
position: absolute;
top: calc(0vh - 500px);
left: 0;
right: 0;
/* pointer-events: none; */
/* top: 50%;
left: 50%;
transform: translate(-50%); */
}
.drop-enter-active {
transition: transform 3s;
}
.drop-enter {
transform: translateY(0vh);
}
.drop-enter-to {
transform: translateY(calc(100vh + 500px));
}
</style>
<div id="app">
<test></test>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component('test', {
template:
`
<div class="card">
<footer class="card-footer">
<a class="card-footer-item" #click="drop">run</a>
</footer>
<transition-group name="drop" v-on:after-enter="remove(index)">
<img src="https://picsum.photos/id/237/200/300" class="image" v-for=" (item, index) in items" :key="index" alt="an image">
</transition-group>
</div>
`
,
data() {
return {
items: []
}
},
methods: {
drop() {
this.items.push(this.item);
},
remove (index) {
this.$delete(this.items, index);
}
}
})
</script>
<script>
const vue = new Vue({
el: '#app'
})
</script>
</body>
</html>
Related
I'm trying to create a simple marketcap checker for crytpo (like coinmarketcap) using coingecko api.
I can fetch the data and render it, no problem with that. And I fetch the data 2 times per minutes.
But now, I would like to check if the new price is higher or lower than the last price.
I do a v-for loop and I pass some data in my "tokenComponent" for rendering the data like this :
<template>
<div id="app">
<div class="container mx-auto">
<div class="pt-6">
<h1 class="text-2xl font-bold">Crypto Monkey Cap</h1>
<div v-for="token in listTokens" :key="token.id">
<div class="py-6">
<token-component
:name="token.name"
:price="token.current_price"
:mcap="token.market_cap"
></token-component>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import TokenComponent from "./components/tokenComponent.vue";
export default {
name: "App",
components: {
TokenComponent,
},
data() {
return {
listTokens: [],
lastPrice: 0
};
},
mounted() {
this.getTokens();
setInterval(() => {
this.getTokens()
}, 30000);
},
methods: {
getTokens() {
fetch("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd")
.then((response) => response.json())
.then((data) => {
this.listTokens = data;
});
}
}
};
</script>
and the tokenComponent :
<template>
<div class="py-4 border-2 rounded-lg">
<div class="flex justify-around">
<h2>{{ name }}</h2>
<h2>{{ price }} $</h2>
<h2>{{ mcap }} $</h2>
</div>
</div>
</template>
<script>
export default {
props: {
name: { required: true },
price: { required: true },
mcap: { required: true }
}
};
</script>
I just would like put a conditionnal class in price data if the last price is higher or lower than the new one...
(I'm new in Vuejs... ;) )
You should store previous prices to calculate if the last price is higher or lower than the new one. Use Array for that.
Added small example using setInterval instead of fetching new prices to display indicators
new Vue({
el: "#app",
data: () => ({
prices: [1]
}),
methods: {
stonks(index) {
if (index > 0) {
return (this.prices[index] - this.prices[index-1]) > 0
? 'green' : 'red'
}
}
},
mounted() {
setInterval(() => {
this.prices.push(Math.floor(Math.random() * 10) + 1)
}, 2000)
}
})
.prices {
display: flex;
flex-direction: row;
padding: 10px;
}
.price {
border:1px solid #bbb;
border-radius: 5px;
padding: 10px;
width: 32px;
height: 32px;
line-height: 32px;
text-align: center;
margin-right: 5px;
position: relative;
}
.stonks {
position: absolute;
background: grey;
border-radius: 50%;
width: 16px;
height: 16px;
top: 0;
right: 0;
margin-top:-8px;
margin-right:-8px
}
.stonks.red { background: red; }
.stonks.green { background: green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="prices">
<div
v-for="(price, index) in prices"
:key="index"
class="price"
>
{{ price }}
<div class="stonks" :class="stonks(index)" />
</div>
</div>
</div>
HTML that is needed to know
<section class="home" id="home">
<div class="max-width">
<div class="home-content">
<div class="text-1">Hello, my name is</div>
<div class="text-2">Hadi Zouhbi</div>
<div class="text-3">And I'm a <span id="headSpan">Developer</span></div>
</div>
</div>
</section>
Here is the needed CSS
.sticky {
padding: 30px 0;
background-color: crimson;
}
.stickyHeadSpan {
color: #fff;
}
Here is the javascript code that is needed
window.addEventListener('scroll' , function(){
if(window.scrollY > 20){
const navbar = document.querySelector('.navbar')
const headSpan = document.querySelector('span')
navbar.classList.add('sticky')
headSpan.classList.add('stickyHeadSpan')
}
})
window.addEventListener('scroll' , () => {
if(window.scrollY === 0) {
const navbar = document.querySelector('.navbar')
navbar.classList.remove('sticky')
}
})
I tried getting the span by the id and still did not work , whenever I scroll down the span is not changing color to white , did I make a mistake somewhere ? I also tried using just span so no id or class and it still did not work , it is really strange . Is there any rule that makes this not work ? I am a beginner at Javascript so I hope you can help me fix this.
window.addEventListener('scroll', function() {
if (window.scrollY > 20) {
const navbar = document.querySelector('.home');
const headSpan = document.querySelector('span');
navbar.classList.add('sticky');
headSpan.classList.add('stickyHeadSpan');
}
})
window.addEventListener('scroll', () => {
if (window.scrollY === 0) {
const navbar = document.querySelector('.home')
navbar.classList.remove('sticky')
}
})
.wrapper{
height: 200vh;
}
.sticky {
padding: 30px 0;
background-color: crimson;
position: sticky;
top: 0;
left: 0;
}
.stickyHeadSpan {
color: #fff;
}
<div class="wrapper">
<section class="home" id="home">
<div class="max-width">
<div class="home-content">
<div class="text-1">Hello, my name is</div>
<div class="text-2">Hadi Zouhbi</div>
<div class="text-3">And I'm a <span id="headSpan">Developer</span></div>
</div>
</div>
</section>
</div>
I want to display a text with my picture when I click on my thumbnail picture and I wrote a method to do something like this, but I don't know why my text can't appear on my web site.
Because I created a method in my JS to display the text under the thumbnails, and I called this function in my template part of JS.
Can you help me please?
Vue.component('carousel', {
template: `
<div class="card-carousel" >
<div class="thumbnails">
<div
v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
#click="activateImage(index)" #click="activateText(index)">
<img :src="image.thumb">
</div>
</div>
<div class="containe-carousel">
<div class="photoshop-screenshot">
<img :src="currentImage" alt="">
</div>
<div class="card-img">
<img :src="currentImage2" alt="">
</div>
</div>
</div>
`,
computed: {
currentImage() {
return this.images[this.activeImage].big;
},
currentImage2() {
return this.images[this.activeImage].big2;
},
currentText (){
return this.texts[this.activeText].text;
}
},
data() {
return {
activeImage: 0,
activeText :0,
}
},
methods: {
activateImage(imageIndex) {
this.activeImage = imageIndex;
},
activeText (imageIndex){
this.activeText = imageIndex;
}
},
props: ['images', 'texts']
})
<script>
var app = new Vue({
el: '#app',
data() {
return {
images: [
{
id: '1',
big: '/images/keyboard1/photoshop-profile.PNG',
big2: '/images/keyboard1/photoshop-screenshot.png',
text : 'photo 1',
thumb: '/images/keyboard1/photoshop-logo.jpg'
},
{
id: '2',
big: '/images/keyboard2/autocad-profile.png',
big2: '/images/keyboard2/autocad-screenshot.png',
text : 'photo 2',
thumb: '/images/keyboard2/autocad-logo.png'
},
{
id: '3',
big: '/images/keyboard3/counterstrike-profile.png',
big2: '/images/keyboard3/counterstrike-screenshot.jpg',
thumb: '/images/keyboard3/counterstrike-logo.png'
},
{
id: '4',
big: '/images/keyboard4/leagueoflegends-profile.png',
big2: '/images/keyboard4/leagueoflegends-screenshot.png',
thumb: '/images/keyboard4/leagueoflegends-logo.jpg'
}
]
}
}
});
</script>
CSS:
.section{
background-color: black;
}
.card-carousel {
user-select: none;
position: relative;
}
.thumbnails {
display: flex;
justify-content: space-evenly;
flex-direction: row;
}
.thumbnail-image {
display: fixed;
align-items: center;
cursor: pointer;
padding: 2px;
}
.thumbnail-image > img {
width: 100%;
height: auto;
transition: all 250ms;
filter: grayscale(100%);
}
.thumbnail-image:selected> img {
box-shadow: 2px 2px 6px 1px rgba(0,0,0, 0.5);
visibility: hidden;
filter: none;
}
.card-img {
position: relative;
}
.card-img > img {
margin: 0 auto;
padding-top: 7%;
z-index: 2;
}
.photoshop-screenshot {
position:absolute;
z-index: 1;
width: 70%;
right:-80px;
bottom:-130px;
}
First you don't need to texts in props object and you have duplicated #click event i cleaned up the unnecessary parts from your code:
Vue.component('carousel', {
template: `
<div class="card-carousel" >
<div class="thumbnails">
<div
v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
#click="activateImage(index)">
<img :src="image.thumb"/>
<caption>{{image.text}}</caption>
</div>
</div>
<div class="containe-carousel">
<div class="photoshop-screenshot">
<img :src="currentImage.big" alt="">
<caption> {{currentImage.text}}</caption>
</div>
<div class="card-img">
<img :src="currentImage2.big2" alt="">
<caption> {{currentImage2.text}}</caption>
</div>
</div>
</div>
`,
computed: {
currentImage() {
return this.images[this.activeImage];
},
currentImage2() {
return this.images[this.activeImage];
}
},
data() {
return {
activeImage: 0,
}
},
methods: {
activateImage(imageIndex) {
this.activeImage = imageIndex;
},
},
props: ['images']
})
There's the demo and the source code , i just used single file component but it doesn't matter
I'm trying to find a way to delay the final part as stated in the title.
My initial JQuery code
var debounce = false;
var elements = document.getElementsByClassName('.Menu')
$('#Option1').click(function() {
if (debounce == true) {return;}
debounce = true;
$('.Menu').each(function(index) {
anim2($(this), index * 250, function() {
if (index != elements.length) {return;}
debounce = false;
})
})
});
This produces what I want to a certain extent but due to the delays and the fact that the display becomes none, I don't get what I truly want.
GIF Representing problem : https://gyazo.com/3d8f46ec3e34dfd7b88738fc00d477e1
The initial fade in works great but on the fade out when the first button disappears the delayed buttons for the other ones shift to the left which is what I'm trying not to let happen.
I tried doing:
var debounce = false;
var isClicked = false;
var elements = document.getElementsByClassName('.Menu')
$('#Option1').click(function() {
if (debounce == true) {return;}
debounce = true;
$('.Menu').each(function(index) {
anim2($(this), index * 250, function() {
if (index != elements.length) {
if (isClicked == false) {
isClicked = true;
$('.Menu').each(function(index) {
$(this).css("display", "none");
$(this).css("opacity", "0");
})
} else {
isClicked = false;
$(this).css("display", "inline-block");
$(this).css("opacity", "1");
}
}
debounce = false;
})
})
});
But it doesn't work and creates bugs. If you need to know the anim2 function it is
function anim2(object, dt, end) {
$(object).stop().delay(dt).fadeToggle({
duration: 1000,
easing: "easeOutQuad",
quene: true,
complete: end
})
}
Just going to post the relevant parts of the LESS in case it might be the cause of it
.invisible {
background: transparent;
border: none;
}
.Hamburger {
background: #pure-white;
width: 100%;
height: 5px;
opacity: 0;
position: absolute;
.rounded
}
#Option1 {
.invisible;
position: absolute;
padding: 0px 0px 0px 0px;
top: 0px;
left: 10px;
height: 100%;
width: 40px;
#TopSpan {
.Hamburger;
top: 10px;
}
#MiddleSpan {
.Hamburger;
top: 20px;
}
#BottomSpan {
.Hamburger;
top: 30px;
}
&:active {
background: #pure-red;
}
}
I have also checked out Delay of a few seconds before display:none and Hide div after a few seconds but delay() won't work since it's an automatic effect
HTML
<!DOCTYPE html>
<html lang="en">
<head class="Setup">
<link rel="stylesheet/less" type="text/css" href="../LESS/core.less"/>
<script src="../JavaScript/less.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
<script type='text/javascript' src="../JavaScript/java.js"></script>
</head>
<body class="Setup">
<div class="Design">
<div class="TopDesign">
<span id="Topbar"></span>
<span id="Minibar">
<button class="Buttons" id="Option1">
<span class="Home" id="TopSpan"></span>
<span class="Home" id="MiddleSpan"></span>
<span class="Home" id="BottomSpan"></span>
</button>
<button class="Buttons Menu" id="Sub1">
<p class="SubText">Source1</p>
</button>
<button class="Buttons Menu" id="Sub2">
<p class="SubText">Source2</p>
</button>
<button class="Buttons Menu" id="Sub3">
<p class="SubText">Source3</p>
</button>
</span>
</div>
<div class="LeftDesign">
<span id="Leftbar">
<img src="" alt="">
</span>
</div>
</div>
</body>
</html>
Here is an answer not using javascript for the animation but CSS:
https://jsfiddle.net/7a1cpu0n/
I know this isn't exactly what you wanted, but it's simpler code and you should be able to apply the concept to your project. Just use CSS transition on the elements you want to show/hide and use javascript to toggle their class.
<ul>
<li>Menu</li>
<li>link1</li>
<li>link2</li>
<li>link3</li>
</ul>
$(document).ready(function(){
$('li:first-child').click(function(){
var time = 250;
$(this).siblings().each(function(){
var el = $(this);
setTimeout( function(){
el.toggleClass('show');
}, time);
time = time+250;
});
});
});
ul li:not(:first-child){
opacity: 0;
}
ul li {
float: left;
padding: 10px;
margin: 10px;
background: #e6e6e6;
list-style: none;
transition: all 1s;
}
ul li.show {
opacity: 1;
}
This is proof of concept.
I am a newbie to angular JS and having problem in adding the spliced value back to the list.
How cam i do so or can i use any other Angular JS function in place of splice to do the need full.
Below is the code i have do.
{
<!DOCTYPE html>
<html>
<head>
<title>Drag Drop + Remove Element</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootswatch/2.3.1/spruce/bootstrap.min.css">
<style>
[ng-drag] {
width: 100px;
height: 100px;
background: red;
color: white;
text-align: center;
padding-top: 40px;
display: block;
}
[ng-drop] {
background: silver;
text-align: center;
display: block;
position: relative;
padding: 20px;
width: 140px;
height: 140px;
float: left;
}
.draglist {
display: inline-block;
margin: 0 auto;
}
[ng-drag].dragging {
opacity: 0.5;
}
[ng-drag].drag-over {
border: solid 1px red;
}
[ng-drop].drag-enter {
border: solid 5px red;
}
</style>
</head>
<body ng-app="ExampleApp">
<div class="row text-center">
<h1>Drag Drop + Remove Element</h1>
</div>
<div class="row text-center" ng-controller="MainCtrl">
<ul class="draglist">
<li ng-repeat="obj in draggableObjects | limitTo:quantity" ng-drop="true" ng-drop-success="onDropComplete($index, $data,$event)" >
<button type="button" class="close" ng-click="removeElement($index,draggableObjects.length)"><span aria-hidden="true" ng-mouseover>×</span></button>
<div ng-drag="true" ng-drag-data="obj" ng-class="obj.name">
{{obj.name}}
</div>
</li>
</ul>
</div>
<!--<script src="http://code.angularjs.org/1.3.15/angular.min.js"></script>-->
<script src="Angular.js"></script>
<script src="ngDraggable.js"></script>
<script>
angular.module('ExampleApp', ['ngDraggable']).
controller('MainCtrl', function ($scope, $window) {
$scope.draggableObjects = [
{name: 'one'},
{name: 'two'},
{name: 'three'},
{name: 'four'},
{name: 'five'},
{name: 'six'},
{name: 'seven'},
{name: 'eight'},
{name: 'nine'},
{name: 'ten'}
];
$scope.quantity = 3;
$scope.showDelete = false;
$scope.removeElement = function(index, objLength){
console.log("objLength::"+objLength);
$scope.showDelete = true;
if(objLength < 4){
console.log("In if");
// $window.location.reload();
// $scope.draggableObjects;
console.log($scope.draggableObjects);
$scope.draggableObjects = $scope.draggableObjects.concat($scope.draggableObjects);
// angular.forEach($scope.draggableObjects, function(objects) {
// $scope.draggableObjects.push(objects);
//// console.log($scope.draggableObjects);
// });
}
else{
$scope.draggableObjects.splice(index,1);
}
};
$scope.onDropComplete = function (index, obj, evt) {
var otherObj = $scope.draggableObjects[index];
var otherIndex = $scope.draggableObjects.indexOf(obj);
$scope.draggableObjects[index] = obj;
$scope.draggableObjects[otherIndex] = otherObj;
};
});
</script>
</body>
</html>
}
Now what i want to do is once the value get spliced from the $scope.draggableObjects i want to add the spliced data again at the end of the code so that a cycle is continued of all the values in scope.
Any help will be welcomed.
Thanks in advance.
just javascript
$scope.draggableObjects.push(obj) // obj prev removed