I’ve been playing around with transitions a bit in Vue. I’ve got a test app that gets from a db then shows in a table format with v-for to populate the table cells. transition-group though, doesn’t seem to work at all. I’ve got:
<table class="table" v-if="showTable">
<thead>
<tr>
<th>#</th>
<th>First Name</th>
<th>Last Name</th>
<th>Username</th>
</tr>
</thead>
<transition-group tag="tbody" enter-active-class="animated fadeInDownBig" leave-active-class="animated fadeOutRight">
<tr v-for="(value, key, index) in detailsData" v-bind:key="key">
<th scope="row">{{value.details_key}}</th>
<td>{{value.first_name}}</td>
<td>{{value.last_name}}</td>
<td><button class="btn btn-danger" #click="deleteEntry(value.details_key, key, $event)">Delete</button></td>
</tr>
</transition-group>
</table>
The classes I’m trying to use are part of Animate.css, they work fine with just tags. I’ve also tried adding a “name” tag and using my own css classes but nothing seems to work.
For first sight, IMO its not working because you are trying to animate table rows - <tr> tags. And this is not possible. Possible solution is to use the CSS display property to simulate <tr> tags but with another tag - <div> for example, but with CSS like this: div { display: table-row } Look for this post, where I show the animated table example, and how I created table without <table>, or any other table related tags.
Vue.component('data-grid', {
template: '#data-grid',
props: ['url', 'columns'],
data () {
return {
users: [],
query: '',
prevKey: 'id',
orderDesc: false
}
},
computed: {
filteredUsers () {
return _.filter(this.users, user =>
_.find(user, prop =>
new RegExp(this.query, 'i').test(prop)
)
)
}
},
methods: {
sortUsers (evt) {
var key = evt.target.dataset.key
if (this.prevKey === key) {
this.users.reverse()
this.orderDesc = !this.orderDesc
} else {
this.users = _.sortBy(this.users, key)
this.orderDesc = false
this.prevKey = key
}
},
updateQuery: _.debounce(function (evt) {
this.query = evt.target.value
}, 300),
clearQuery () {
this.query = ''
},
onCreate (elm) {
elm.style.opacity = 0
},
async onData (elm) {
this.users = await axios
.get(this.url)
.then(res => res.data)
Velocity(elm, "fadeIn", {duration: 600})
}
}
})
new Vue({
el: '#app'
})
.data-grid {
width: 98%;
margin: 10px auto;
padding: 2px;
background-color: white;
border: 2px solid #3F51B5;
overflow: hidden;
}
.table {
display: table;
width: 100%;
font-size: 13px;
font-family: Arial, sans-serif;
color: #263238;
cursor: pointer;
}
.thead {
display: table-header-group;
}
.tbody {
display: table-row-group;
}
.tr {
display: table-row;
}
.td {
display: table-cell;
position: relative;
}
.tr .td:not(:last-child) {
border-right: 2px solid white;
}
.thead .td {
padding: 5px 14px;
background-color: #3F51B5;
color: white;
font-weight: bold;
text-align: center;
}
.tbody .td {
padding: 4px;
color: #263238;
text-align: center;
}
.tbody .tr:hover .td {
background-color: #C5CAE9;
}
.tbody .tr:hover .td:not(:last-child) {
border-right: 2px solid #C5CAE9;
}
.tools {
margin: 10px;
box-sizing: border-box;
}
.tools:after {
content: "";
display: block;
clear: both;
}
.search {
float: right;
}
.arrow {
display: inline-block;
position: absolute;
width: 0;
height: 0;
margin-left: 5px;
margin-top: 5px;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
transition: all .6s;
}
.asc {
border-bottom: 6px solid white;
}
.desc {
border-top: 6px solid white;
}
.users-move {
transition: transform .6s;
}
.users-enter-active, .users-leave-active {
transition: all .6s;
}
.users-enter, .users-leave-to {
opacity: 0;
transform: translateY(30px);
}
<div id="app">
<data-grid
url="https://api.mockaroo.com/api/b5f38710?count=8&key=cdbbbcd0"
:columns="{id: 'ID', nick: 'Nick name', first: 'First name', last: 'Last name'}"
></data-grid>
</div>
<template id="data-grid">
<transition
appear
v-on:before-appear="onCreate"
v-on:appear="onData"
>
<div class="data-grid">
<div class="tools">
<div class="search">
<input
type="text"
#input="updateQuery"
:value="query"
placehorder="search..."
>
<button class="clear" #click="clearQuery">clear</button>
</div>
</div>
<div class="table">
<div class="thead" #click="sortUsers">
<div class="tr">
<span v-for="(col, key) in columns" class="td" :data-key=key>
{{ col }}
<span
v-if="prevKey === key"
:class="['arrow', orderDesc ? 'desc' : 'asc']">
</span>
</span>
</div>
</div>
<transition-group name="users" tag="div" class="tbody">
<div class="tr" v-for="row in filteredUsers" :key="row.id">
<span class="td" v-for="column in row">{{ column }}</span>
</div>
</transition-group>
</div>
</div>
</transition>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.5.0/velocity.min.js"></script>
<script src="https://unpkg.com/underscore#1.8.3/underscore-min.js"></script>
<script src="https://unpkg.com/vue#2.4.4/dist/vue.min.js"></script>
<script src="https://unpkg.com/axios#0.16.2/dist/axios.min.js"></script>
Related
Good day,
I am trying to split and Axios request into 2 columns when above 720px and 1 column when under 720px.
I managed to achieve this by using CSS with some height: 200vh, display flex, and so on to force it to split into 2 columns, but this is not the correct approach.
There is a way of doing it with computed propriety but I just can't get it to work.
My full code is below:
<template>
<div class="container">
<button #click="getPosts" class="btn btn-info btn-lg mb-5">
Load List
</button>
<h4 v-if="errorMsg">{{ errorMsg }}</h4>
<!-- <table class="table table-striped table-hover">
<tbody class="list__wrapper">
<tr v-for="post in posts" :key="post.id">
<td>{{ post.title }}</td>
</tr>
</tbody>
</table> -->
<div class="container">
<div class="col" v-for="column in columns" :key="column.id">
<div class="item-container" v-for="post in column" :key="post.id">
{{ post.title }}
</div>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "PostList",
data() {
return {
posts: [],
errorMsg: "",
cols: 2,
};
},
computed: {
columns() {
let columns = [];
let mid = Math.ceil(this.post.length / this.cols);
for (let col = 0; col < this.cols; col++) {
columns.push(this.post.slice(col * mid, col * mid + mid));
}
return columns;
},
},
methods: {
getPosts() {
axios
.get("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
console.log(response.data);
this.posts = response.data;
})
.catch((error) => {
console.log(error);
this.errorMsg = "Error retrieving data";
});
},
},
};
</script>
<style scoped>
body {
margin: 0;
padding: 0;
font-family: "Ubuntu", sans-serif;
}
.list {
list-style: none;
}
.list__wrapper {
display: flex;
flex-direction: column;
flex-wrap: wrap;
/* height: 510vh; */
}
.table-hover tbody tr:hover {
background-color: #759c99;
cursor: pointer;
padding: 10px;
transition: 0.3s;
}
.table td {
padding: 5px;
border: none;
}
#media screen and (min-width: 720px) {
tbody {
-moz-column-count: 4;
-moz-column-gap: 20px;
-webkit-column-count: 4;
-webkit-column-gap: 20px;
column-count: 2;
column-gap: 20px;
}
.list__wrapper {
/* height: 150vh; */
}
}
/* test */
.container {
display: flex;
border: 1px solid;
}
.col {
margin: 10px;
border: 1px solid;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.item-container {
border: 1px solid;
padding: 5px;
margin: 5px;
}
</style>
I fixed it eventualy from CSS:
.list__wrapper {
display: block;
column-count: 2;
column-gap: 40px;
column-rule: 1px double #759c99;
}
I'm making a small page for the product based on this tutorial. On the page I have a product component, where I keep a "add to cart" button. The card itself, however, is separated from the component and located inside index.html, that's why my cart property is being kept inside vue app (where app is an id of my root div in index.html).
Problem: I need my "add to cart" button to increment the number in the cart itself. I can't really understand how can I do this using addToCart and updateCart methods, as shown in the tutorial.
Can anyone help me out with this issue? I will appreciate any help! Thanks in advance!
Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div id="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p>Shipping: {{ shipping }}</p>
<p v-if="inStock">In Stock</p>
<p v-else>Out of Stock</p>
<h2>Details</h2>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<h3>Colors:</h3>
<div v-for="(variant,index) in variants" :key="variant.variantId">
<div class="color-box" :style="{ backgroundColor: variant.variantColor }" #mouseover="updateProduct(index)"></div>
</div>
<button :class="{ disabledButton: !inStock }" v-on:click="add-to-cart" :disabled="!inStock">Add to Cart</button>
</div>
</div>
`,
data() {
return {
product: "Socks",
brand: "Vue Mastery",
selectedVariant: 0,
details: ["80% cotton", "20% polyester", "Gender-neutral"],
variants: [
{
variantId: 2234,
variantQuantity: 15,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantQuantity: 0,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
}
]
}
},
methods: {
addToCart() {
this.$emit('add-to-cart')
},
updateProduct(index) {
this.selectedVariant = index
}
},
computed: {
title() {
return this.brand + ' ' + this.product
},
image() {
return this.variants[this.selectedVariant].variantImage
},
inStock() {
if (this.variants[this.selectedVariant].variantQuantity > 0) {
return true
} else {
return false
}
},
shipping() {
if (this.premium) {
return "Free"
} else {
return 2.99
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: 0
},
methods: {
updateCart() {
this.cart += 1
}
}
})
body {
font-family: tahoma;
color:#282828;
margin: 0px;
}
.nav-bar {
background: linear-gradient(-90deg, #84CF6A, #16C0B0);
height: 60px;
margin-bottom: 15px;
}
.product {
display: flex;
flex-flow: wrap;
padding: 1rem;
}
img {
border: 1px solid #d8d8d8;
width: 70%;
margin: 40px;
box-shadow: 0px .5px 1px #d8d8d8;
}
.product-image {
width: 80%;
}
.product-image,
.product-info {
margin-top: 10px;
width: 50%;
}
.color-box {
width: 40px;
height: 40px;
margin-top: 5px;
}
.cart {
margin-right: 25px;
float: right;
border: 1px solid #d8d8d8;
padding: 5px 20px;
}
button {
margin-top: 30px;
border: none;
background-color: #1E95EA;
color: white;
height: 40px;
width: 100px;
font-size: 14px;
}
.disabledButton {
background-color: #d8d8d8;
}
.review-form {
width: 400px;
padding: 20px;
margin: 40px;
border: 1px solid #d8d8d8;
}
input {
width: 100%;
height: 25px;
margin-bottom: 20px;
}
textarea {
width: 100%;
height: 60px;
}
.tab {
margin-left: 20px;
cursor: pointer;
}
.activeTab {
color: #16C0B0;
text-decoration: underline;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewpoint" content="width=devide-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Vue app</title>
</head>
<body>
<div class="nav-bar"></div>
<div id="app">
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
<product :premium="premium" #add-to-cart="updateCart"></product>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="prior.js"></script>
</body>
</html>
Change This:
<button :class="{ disabledButton: !inStock }" v-on:click="add-to-cart" :disabled="!inStock">Add to Cart</button>
To be this:
<button :class="{ disabledButton: !inStock }" v-on:click="addToCart" :disabled="!inStock">Add to Cart</button>
So addToCart: and this function will emit add-to-cart event
Also you can immediately run emmit onClick without adding function:
<button :class="{ disabledButton: !inStock }" v-on:click="$emit('add-to-cart')" :disabled="!inStock">Add to Cart</button>
Vue.component('product', {
props: {
premium: {
type: Boolean,
required: true
}
},
template: `
<div id="product">
<div class="product-image">
<img :src="image" />
</div>
<div class="product-info">
<h1>{{ title }}</h1>
<p>Shipping: {{ shipping }}</p>
<p v-if="inStock">In Stock</p>
<p v-else>Out of Stock</p>
<h2>Details</h2>
<ul>
<li v-for="detail in details">{{ detail }}</li>
</ul>
<h3>Colors:</h3>
<div v-for="(variant,index) in variants" :key="variant.variantId">
<div class="color-box" :style="{ backgroundColor: variant.variantColor }" #mouseover="updateProduct(index)"></div>
</div>
<button :class="{ disabledButton: !inStock }" v-on:click="addToCart" :disabled="!inStock">Add to Cart</button>
</div>
</div>
`,
data() {
return {
product: "Socks",
brand: "Vue Mastery",
selectedVariant: 0,
details: ["80% cotton", "20% polyester", "Gender-neutral"],
variants: [
{
variantId: 2234,
variantQuantity: 15,
variantColor: "green",
variantImage: "./assets/vmSocks-green.jpg"
},
{
variantId: 2235,
variantQuantity: 0,
variantColor: "blue",
variantImage: "./assets/vmSocks-blue.jpg"
}
]
}
},
methods: {
addToCart() {
this.$emit('add-to-cart')
},
updateProduct(index) {
this.selectedVariant = index
}
},
computed: {
title() {
return this.brand + ' ' + this.product
},
image() {
return this.variants[this.selectedVariant].variantImage
},
inStock() {
if (this.variants[this.selectedVariant].variantQuantity > 0) {
return true
} else {
return false
}
},
shipping() {
if (this.premium) {
return "Free"
} else {
return 2.99
}
}
}
})
var app = new Vue({
el: '#app',
data: {
premium: true,
cart: 0
},
methods: {
updateCart() {
this.cart += 1
}
}
})
body {
font-family: tahoma;
color:#282828;
margin: 0px;
}
.nav-bar {
background: linear-gradient(-90deg, #84CF6A, #16C0B0);
height: 60px;
margin-bottom: 15px;
}
.product {
display: flex;
flex-flow: wrap;
padding: 1rem;
}
img {
border: 1px solid #d8d8d8;
width: 70%;
margin: 40px;
box-shadow: 0px .5px 1px #d8d8d8;
}
.product-image {
width: 80%;
}
.product-image,
.product-info {
margin-top: 10px;
width: 50%;
}
.color-box {
width: 40px;
height: 40px;
margin-top: 5px;
}
.cart {
margin-right: 25px;
float: right;
border: 1px solid #d8d8d8;
padding: 5px 20px;
}
button {
margin-top: 30px;
border: none;
background-color: #1E95EA;
color: white;
height: 40px;
width: 100px;
font-size: 14px;
}
.disabledButton {
background-color: #d8d8d8;
}
.review-form {
width: 400px;
padding: 20px;
margin: 40px;
border: 1px solid #d8d8d8;
}
input {
width: 100%;
height: 25px;
margin-bottom: 20px;
}
textarea {
width: 100%;
height: 60px;
}
.tab {
margin-left: 20px;
cursor: pointer;
}
.activeTab {
color: #16C0B0;
text-decoration: underline;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewpoint" content="width=devide-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Vue app</title>
</head>
<body>
<div class="nav-bar"></div>
<div id="app">
<div class="cart">
<p>Cart({{ cart }})</p>
</div>
<product :premium="premium" #add-to-cart="updateCart"></product>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="prior.js"></script>
</body>
</html>
I am stuck in a logical problem.
I have an array where i am stacking items with array.push() where came up from a user's Input.
Problem is now:
How can i print these items to the DOM? ATM i am doing this,
function getInput(operator, description, value) {
// SAVE IN INCOME IF "+" IS CHOOSEN (DEFAULT)
if (addType.value == 'inc') {
let op = incomeArr['operator'] = operator;
let des = incomeArr['description'] = description;
let val = incomeArr['value'] = value;
incomeArr.push([op, des, val]);
return incomeArr;
}
}
Creating a associative array in the getInput();
First attempt to print this data into the DOM looked like this:
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
for (var i = 0; i < expenseArr.length; i++) {
incomeItemSpan;
incomeList.append(incomeItemSpan);
}
}
My problem here is that my forLoop index condition is messing up because of the "everytime function call" the value which is printed out will be printed twice in the next function call of this. the index start again at 0 and even with a out of function loop counter this will not work.
[![gave-input][1]][1]
So the next attempt was:
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
incomeArr.forEach(() => {
incomeList.innerHTML = incomeItemSpan;
});
}
i tried it with a forEach and the problem here is, i have absolutely no idea how to print out the incomeItemSpan without a innerHTML. I want a list of items in the DOM which are stacked from top down, every line is a new item from the array like i would use item.append(), but HTML wont work in a append().
How can i do this?
/* TODO:
- Add Eventlistener for Submit a +/- Value
- if + {add 1. into INCOME section} + Set INCOME in header to the amount
of all incomes added
if - {same like if+ just for -}
- create a update DOM function to update the visualisation of the calculations
- INCOME AND EXPENSES should be a Array
- add a prototype function to remove entrys from INCOME and EXPENSES, use
indexOf to get the index item and remove with array.splice().
- calc every expense with INCOME to get a % value of how much this entry is
% related to the max INCOME
*/
// VARS:
let addType = document.querySelector('.add__type');
let description = document.querySelector('.add__description');
let addValue = document.querySelector('.add__value');
let incomeArr = [];
let expenseArr = [];
// EVENTLISTENER Constructor:
function EventListner(selector, listner, fnt) {
this.selector = selector;
this.listner = listner;
this.fnt = fnt;
document.querySelector(selector).addEventListener(listner, fnt);
};
// getInput VALUES FROM USER Constructor:
function getInput(operator, description, value) {
// SAVE IN INCOME IF "+" IS CHOOSEN (DEFAULT)
if (addType.value == 'inc') {
let op = incomeArr['operator'] = operator;
let des = incomeArr['description'] = description;
let val = incomeArr['value'] = value;
incomeArr.push([op, des, val]);
// TODO: WHAT AFTER SAVING?
return incomeArr;
}
// SAVE IN EXPENSE IF "-" IS CHOOSEN
if (addType.value == 'exp') {
let op = expenseArr['operator'] = operator;
let des = expenseArr['description'] = description;
let val = expenseArr['value'] = value;
expenseArr.push([op, des, val]);
// TODO: WHAT AFTER SAVING?
return expenseArr;
}
};
// STUCK AS FUCK!
function printToDOM(item) {
// every function call should run this once to update the UI
const incomeList = document.querySelector('.income__list');
const expenseList = document.querySelector('.expenses__list');
let incomeItemSpan = `<span> ${item.description}: ${item.value} </span></br>`;
let expenseItemSpan = `<span> ${item.description}: ${item.value} </span> </br>`;
incomeArr.forEach(() => {
incomeList.innerHTML = incomeItemSpan;
});
// for (var i = 0; i < incomeArr.length; i++) {
// console.log([i]);
// incomeItemSpan;
// incomeList.append(incomeItemSpan);
// }
// console.log(incomeItemSpan);
//
// incomeList.append(expenseArr);
// incomeArr.toString();
// expenseArr.toString();
// incomeList.innerHTML = incomeItemSpan;
};
const main = (function() {
// EVENTLISTENERS
const clickListener = new EventListner('.add__btn', 'click', () => {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
getInput(addType.value, description.value, addValue.value);
});
const enterKeyListener = new EventListner('.add__value', 'keypress', (e) => {
let testArray = [];
for (var i = 0; i < testArray.length; i++) {
testArray[i] = [i];
console.log(testArray[i]);
}
testArray.push('item');
if (e.keyCode == 13) {
if (description.value == '' || addValue.value == '') {
// MAKE SURE DESCRIPTION AND VALUE IS NOT EMPTY
alert('description and value can\'t be empty');
return;
}
// ON ENTER SAVE VALUES IN AN ARRAY
// IF PLUS INTO incomeArr, ON MINUS INTO expenseArr
// getInput(addType.value, description.value, addValue.value);
printToDOM(getInput(addType.value, description.value, addValue.value));
}
});
}());
//
/**********************************************
*** GENERAL
**********************************************/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.clearfix::after {
content: "";
display: table;
clear: both;
}
body {
color: #555;
font-family: Open Sans;
font-size: 16px;
position: relative;
height: 100vh;
font-weight: 400;
}
.right { float: right; }
.red { color: #FF5049 !important; }
.red-focus:focus { border: 1px solid #FF5049 !important; }
/**********************************************
*** TOP PART
**********************************************/
.top {
height: 40vh;
background-image: linear-gradient(rgba(0, 0, 0, 0.35), rgba(0, 0, 0, 0.35)), url(back.png);
background-size: cover;
background-position: center;
position: relative;
}
.budget {
position: absolute;
width: 350px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
}
.budget__title {
font-size: 18px;
text-align: center;
margin-bottom: 10px;
font-weight: 300;
}
.budget__value {
font-weight: 300;
font-size: 46px;
text-align: center;
margin-bottom: 25px;
letter-spacing: 2px;
}
.budget__income,
.budget__expenses {
padding: 12px;
text-transform: uppercase;
}
.budget__income {
margin-bottom: 10px;
background-color: #28B9B5;
}
.budget__expenses {
background-color: #FF5049;
}
.budget__income--text,
.budget__expenses--text {
float: left;
font-size: 13px;
color: #444;
margin-top: 2px;
}
.budget__income--value,
.budget__expenses--value {
letter-spacing: 1px;
float: left;
}
.budget__income--percentage,
.budget__expenses--percentage {
float: left;
width: 34px;
font-size: 11px;
padding: 3px 0;
margin-left: 10px;
}
.budget__expenses--percentage {
background-color: rgba(255, 255, 255, 0.2);
text-align: center;
border-radius: 3px;
}
/**********************************************
*** BOTTOM PART
**********************************************/
/***** FORM *****/
.add {
padding: 14px;
border-bottom: 1px solid #e7e7e7;
background-color: #f7f7f7;
}
.add__container {
margin: 0 auto;
text-align: center;
}
.add__type {
width: 55px;
border: 1px solid #e7e7e7;
height: 44px;
font-size: 18px;
color: inherit;
background-color: #fff;
margin-right: 10px;
font-weight: 300;
transition: border 0.3s;
}
.add__description,
.add__value {
border: 1px solid #e7e7e7;
background-color: #fff;
color: inherit;
font-family: inherit;
font-size: 14px;
padding: 12px 15px;
margin-right: 10px;
border-radius: 5px;
transition: border 0.3s;
}
.add__description { width: 400px;}
.add__value { width: 100px;}
.add__btn {
font-size: 35px;
background: none;
border: none;
color: #28B9B5;
cursor: pointer;
display: inline-block;
vertical-align: middle;
line-height: 1.1;
margin-left: 10px;
}
.add__btn:active { transform: translateY(2px); }
.add__type:focus,
.add__description:focus,
.add__value:focus {
outline: none;
border: 1px solid #28B9B5;
}
.add__btn:focus { outline: none; }
/***** LISTS *****/
.container {
width: 1000px;
margin: 60px auto;
}
.income {
float: left;
width: 475px;
margin-right: 50px;
}
.expenses {
float: left;
width: 475px;
}
h2 {
text-transform: uppercase;
font-size: 18px;
font-weight: 400;
margin-bottom: 15px;
}
.icome__title { color: #28B9B5; }
.expenses__title { color: #FF5049; }
.item {
padding: 13px;
border-bottom: 1px solid #e7e7e7;
}
.item:first-child { border-top: 1px solid #e7e7e7; }
.item:nth-child(even) { background-color: #f7f7f7; }
.item__description {
float: left;
}
.item__value {
float: left;
transition: transform 0.3s;
}
.item__percentage {
float: left;
margin-left: 20px;
transition: transform 0.3s;
font-size: 11px;
background-color: #FFDAD9;
padding: 3px;
border-radius: 3px;
width: 32px;
text-align: center;
}
.income .item__value,
.income .item__delete--btn {
color: #28B9B5;
}
.expenses .item__value,
.expenses .item__percentage,
.expenses .item__delete--btn {
color: #FF5049;
}
.item__delete {
float: left;
}
.item__delete--btn {
font-size: 22px;
background: none;
border: none;
cursor: pointer;
display: inline-block;
vertical-align: middle;
line-height: 1;
display: none;
}
.item__delete--btn:focus { outline: none; }
.item__delete--btn:active { transform: translateY(2px); }
.item:hover .item__delete--btn { display: block; }
.item:hover .item__value { transform: translateX(-20px); }
.item:hover .item__percentage { transform: translateX(-20px); }
.unpaid {
background-color: #FFDAD9 !important;
cursor: pointer;
color: #FF5049;
}
.unpaid .item__percentage { box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1); }
.unpaid:hover .item__description { font-weight: 900; }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:100,300,400,600" rel="stylesheet" type="text/css">
<link href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
<link type="text/css" rel="stylesheet" href="style.css">
<title>Budgety</title>
</head>
<body>
<div class="top">
<div class="budget">
<div class="budget__title">
Available Budget in <span class="budget__title--month">%Month%</span>:
</div>
<div class="budget__value">+ 0</div>
<div class="budget__income clearfix">
<div class="budget__income--text">Income</div>
<div class="right">
<div class="budget__income--value">+ 0</div>
<div class="budget__income--percentage"> </div>
</div>
</div>
<div class="budget__expenses clearfix">
<div class="budget__expenses--text">Expenses</div>
<div class="right clearfix">
<div class="budget__expenses--value">- 0</div>
<div class="budget__expenses--percentage">0%</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="add">
<div class="add__container">
<select class="add__type">
<option value="inc" selected>+</option>
<option value="exp">-</option>
</select>
<input type="text" class="add__description" placeholder="Add description">
<input type="number" class="add__value" placeholder="Value">
<button class="add__btn"><i class="ion-ios-checkmark-outline"></i></button>
</div>
</div>
<div class="container clearfix">
<div class="income">
<h2 class="icome__title">Income</h2>
<div class="income__list">
<!-- <div class="item clearfix" id="income-0">
<div class="item__description"></div>
<div class="right clearfix">
<div class="item__value"></div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
<div class="item clearfix" id="income-1">
<div class="item__description">Sold car</div>
<div class="right clearfix">
<div class="item__value">+ 1,500.00</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div> -->
</div>
</div>
<div class="expenses">
<h2 class="expenses__title">Expenses</h2>
<div class="expenses__list">
<!--
<div class="item clearfix" id="expense-0">
<div class="item__description">Apartment rent</div>
<div class="right clearfix">
<div class="item__value">- 900.00</div>
<div class="item__percentage">21%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
<div class="item clearfix" id="expense-1">
<div class="item__description">Grocery shopping</div>
<div class="right clearfix">
<div class="item__value">- 435.28</div>
<div class="item__percentage">10%</div>
<div class="item__delete">
<button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
</div>
</div>
</div>
-->
</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Is that exactly what you're looking for?
const arr = new Array();
function addList() {
const el = document.querySelector("input[name=user]")
const val = el.value
arr.push(val);
el.value = null;
return false;
}
function showList() {
for (let i = 0; i < arr.length; i++) {
const listElement = document.createElement("li");
listElement.textContent = arr[i]
document.body.appendChild(listElement);
}
}
<form onsubmit="return addList()">
<input type="text" name="user">
<button>Add list item</button>
</form><br>
<button class="showList" onclick="showList()">Show list</button>
<ul class="list"></ul>
For creating a list of items, create a ordered/unordered list then append each item to this list in forEach and finally set the html to ordered//unordered list.
Example:
var ol=$('<ol></ol>');
incomeArr.forEach(() => {
ol.append($('<li></li>').text(`${item.description}: ${item.value}`));
});
$('#display').html(ol);
The Problem
List.js isn't properly sorting numbers on my HTML-Table. This is happening because I'm using inner-HTML on the place of numbers, sort works fine if I just put the numbers and not an ID.
Why not just put the numbers on the table like you said?
I admit that's the best solution, it gains on performance and don't use any javascript, BUT :
I need to modify the data a couple of times a day.
I intend to use and display the data in other places as well, and not just the table.
And in consequence, a pain to modify the data on all my HTML pages, where I could modify just the .js and them, all HTML would be updated.
What I want:
Make the button "Numbers" properly sort data inside of each:
document.getElementById("mushroom_N").innerHTML = "some_number";
and not the actual ID name. (in this case, each "mushroom_N")
Conditions:
A solution that uses List.js, and not a different plugin.
I'm not using jQuery, the ideal solution would be one that also don't use it.
My code is HERE on codepen if you prefer.
Thanks for your attention!
var options = {
valueNames: ['name', 'number']
};
var userList = new List('users', options);
function myFunction() {
document.getElementById("demo0").innerHTML = 10;
document.getElementById("demo1").innerHTML = 1000;
document.getElementById("demo2").innerHTML = 1;
document.getElementById("demo3").innerHTML = 100;
}
.list {
font-family: sans-serif;
}
td {
padding: 10px;
border: solid 1px #eee;
}
input {
border: solid 1px #ccc;
border-radius: 5px;
padding: 7px 14px;
margin-bottom: 10px
}
input:focus {
outline: none;
border-color: #aaa;
}
.sort {
padding: 8px 30px;
border-radius: 6px;
border: none;
display: inline-block;
color: #fff;
text-decoration: none;
background-color: #f44336;
height: 30px;
}
.sort:hover {
text-decoration: none;
background-color: #b71c1c;
}
.sort:focus {
outline: none;
}
.sort:after {
display: inline-block;
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid transparent;
content: "";
position: relative;
top: -10px;
right: -5px;
}
.sort.asc:after {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 5px solid #fff;
content: "";
position: relative;
top: 4px;
right: -5px;
}
.sort.desc:after {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #fff;
content: "";
position: relative;
top: -4px;
right: -5px;
}
<html>
<html lang="en">
<head>
</head>
<body onload="myFunction()">
<div id="users">
<!-- "Search" -->
<input class="search" placeholder="Search" />
<!-- "Sort Buttons" -->
<button class="sort" data-sort="name">A -> Z</button>
<button class="sort" data-sort="number">Numbers</button>
<!-- "The List" -->
<ul class="list">
<li>
<h3 class="name">Mario</h3>
<span class="number"><p id="demo3"></p></span>
</li>
<li>
<h3 class="name">Luigi</h3>
<span class="number"><p id="demo1"></p></span>
</li>
<li>
<h3 class="name">Peach</h3>
<span class="number"><p id="demo2"></p></span>
</li>
<li>
<h3 class="name">Toad</h3>
<span class="number"><p id="demo0"></p></span>
</li>
</ul>
</div>
<!-- "List.js" -->
<script src="//cdnjs.cloudflare.com/ajax/libs/list.js/1.5.0/list.min.js"></script>
</body>
</html>
Welp, I did it!
You just need to create an array and put the elements inside of another one. The Codepen for someone interested or having the same problem of mine.
var mushs = [
"100", // Mario
"10", // Luigi
"1000", // Peach
"1" // Toad
// A change in the values will affect all places at the same time.
];
var options = {
valueNames: ['name', 'mushrooms'],
item: '<tr><td class="mdl-data-table__cell--non-numeric name"></td><td class="mushrooms"></td></tr>'
};
var values = [{
name: 'Mario',
mushrooms: mushs[0]
},
{
name: 'Luigi',
mushrooms: mushs[1]
},
{
name: 'Peach',
mushrooms: mushs[2]
},
{
name: 'Toad',
mushrooms: mushs[3]
}
];
var userList = new List('users', options, values);
document.getElementById("mushroom_0").innerHTML = mushs[0];
document.getElementById("mushroom_1").innerHTML = mushs[1];
document.getElementById("mushroom_2").innerHTML = mushs[2];
document.getElementById("mushroom_3").innerHTML = mushs[3];
body {
margin: 0;
padding: 20px;
}
input {
border: solid 1px #ccc;
border-radius: 5px;
padding: 7px 14px;
margin-bottom: 10px
}
input:focus {
outline: none;
border-color: #aaa;
}
<div id="users">
<input class="search" placeholder="Search">
<table class="mdl-data-table mdl-js-data-table">
<div id="mdl-table">
<thead>
<tr>
<th class="mdl-data-table__cell--non-numeric">
<button class="mdl-button mdl-js-button mdl-button--icon sort" data-sort="name">
<i class="mdl-color-text--red-500 material-icons">sort_by_alpha</i>
</button>
</th>
<th>
<button class="mdl-button mdl-js-button mdl-button--icon sort" data-sort="mushrooms">
<i class="mdl-color-text--red-500 material-icons">swap_vertical_circle</i>
</button>
</th>
</tr>
</thead>
<tbody class="list"></tbody>
</div>
</table>
</div>
<p></p>
<div>Hey, I know that Mario got <span id="mushroom_0"></span> mushrooms, also, Luigi got <span id="mushroom_1"></span> of them, Peach was greed and got at least <span id="mushroom_2"></span> mushs, and Toad got only <span id="mushroom_3"></span>, himself.</div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/material-design-lite#1.3.0/material.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script src="https://cdn.jsdelivr.net/npm/material-design-lite#1.3.0/dist/material.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/javve/list.js#1.5.0/dist/list.min.js"></script>
I've created a search function that shows data in my table. However I want to only show one row as a result. I was wondering if anyone knows of any code that would only show one row as a result, rather than multiple rows.
My current HTML:
<input class="search selectable" placeholder="Find any film, TV series or video game release date..." data-column="0" type="search">
<table class="tablesorter">
<thead>
<tr>
<th>Name</th>
<th>Release date</th>
<th>Starring</th>
<th>Genre</th>
<th>Age rating</th>
</tr>
</thead>
<tbody>
<tr>
<td>The Human Centipede 3 (Final Sequence)</td>
<td>
<ul>
<li>Wednesday 1st July</li>
</ul>
</td>
<td>
<ul>
<li>...</li>
</ul>
</td>
<td>
<ul>
<li>Action</li>
</ul>
</td>
<td>
<ul>
<li>18</li>
</ul>
</td>
</tr>
</tbody>
</table>
CSS:
html {
position: fixed;
height: fixed;
background-color: #bc3030;
min-height: fit;
}
table {
width: 663.5px;
}
form {
display: inline-block;
border-radius: 5px;
background-color: #bc3030;
background-image: -ms-linear-gradient(top, #EDE5DB 0%, #DCD0C2 80%);
background-image: -moz-linear-gradient(top, #EDE5DB 0%, #DCD0C2 80%);
background-image: -webkit-linear-gradient(top, #EDE5DB 0%, #DCD0C2 80%);
background-image: linear-gradient(top, #EDE5DB 0%, #DCD0C2 8a0%);
box-shadow: inset 0 1px #F6F2EC, 0 3px 2px #E5E5E5;
}
input {
border: 2px solid #333333;
width: 529px;
height: 50px;
padding-left: 132px;
font-family:"Raleway";
font-size: 20px;
font-weight: normal;
font-weight: bold;
font-weight: 1000;
background: #fff url(http://static.wixstatic.com/media/97c989_1b9f42901df24660a8a3200c1abb366e.png_srz_p_112_61_75_22_0.50_1.20_0.0 0_png_srz) no-repeat 0.1px;
}
td, th {
width: 550px;
border-bottom: 0px solid #bc3030;
color: #000;
padding: 5px 21px;
font-family:"raleway";
font-weight: normal;
font-weight: bold;
font-weight: 10;
font-size: 18px;
}
thead div {
color: #fff;
padding: 0xpx;
margin-left: 0px;
line-height: normal;
border-left: 0px solid #333333;
}
tbody, td {
background: #fff;
}
Javascript:
document.ontouchmove = function (event) {
event.preventDefault();
}
$('table').on('filterEnd', function () {
if (this.config.lastSearch.join('') === '') {
$(this).children('tbody').children().addClass('filtered');
}
});
$(function () {
var $table = $('table').tablesorter({
theme: '',
widgets: ["zebra", "filter"],
widgetOptions: {
filter_columnFilters: false,
filter_saveFilters: true,
filter_reset: '.reset'
}
});
$.tablesorter.filter.bindSearch($table, $('.search'));
$('select').change(function () {
$('.selectable').attr('data-column', $(this).val());
$.tablesorter.filter.bindSearch($table, $('.search'), false);
});
});
I would not recommend doing this because it is not what a user expects when they search.
If you had to do it, then use this code (demo):
$(function () {
$('table')
.tablesorter({
theme: 'blue',
widgets: ['zebra', 'filter']
})
.on('filterEnd', function(e, config ){
if ( config.lastSearch.join('') !== '' ) {
config.$table
.find('tbody tr')
.not('.filtered') // rows not hidden
.filter(':gt(0)') // target rows > first
.addClass('filtered'); // hide
}
});
});
I would also recommend adding a note, like I did in the demo, telling the user what to expect.