I've built a small stacked bar visual just using floated divs that underneath is bound to some data using knockout. What I want to be able to do is to animate changes in the size of these stacks when the data changes.
I've managed to do this in the general case, so of the 4 bars that I've got, 3 of them transition correctly. The problem is my final bar seems to ignore the transition and instantly re-sizes and I can't understand why. Here's a picture of the before/during/after states:
The way that I've defined this transition is simply via css
-webkit-transition: width 1s;
transition: width 1s;
The width of the bars is a computed value, calculating the percentage of items, so each bar should have it's width defined as a percentage. Although the red bar is calculated differently to the other 3 bars, I don't see why that should affect the transition.
What I find quite strange, is that if I modify the width through the developer console for example, then the bar does correctly animate. I'm wondering if anyone can suggest why this might be the case?
var vm = (function generateModel() {
var data = {
name: "Sign-off",
id: "XX",
values: [{ text: "Signed-off", count: 150, color: "#5fb5cc" },
{ text: "Submitted", count: 90, color: "#75d181" },
{ text: "Not Submitted", count: 75, color: "#f8a25b" }
],
aggregates: {
count: 650
}
};
// Create a view model directly from the data which we will update
var vm = ko.mapping.fromJS(data);
// Add a computed value to calculate percentage
vm.values().forEach(function (d) {
d.percentage = ko.computed(function () {
return d.count() / vm.aggregates.count() * 100;
});
});
// Create a
vm.allValues = ko.computed(function() {
var values = [];
var count = 0;
var total = vm.aggregates.count();
debugger;
// Add each of these results into those that will be returned
vm.values().forEach(function(d) {
values.push(d);
count += d.count();
});
// Create an other category for everything else
values.push({
text: ko.observable("Other"),
count: ko.observable(total - count),
percentage: ko.observable((total - count) / total * 100),
color: ko.observable("#ff0000")
});
return values;
});
return vm;
})();
ko.applyBindings(vm);
setTimeout(function() {
vm.values()[0].count(90);
vm.values()[1].count(40);
vm.values()[2].count(35);
vm.aggregates.count(3550);
}, 3000);
body {
background: rgb(40, 40, 40);
}
.spacer {
height: 230px;
}
.cards {
float: right;
}
/* Small Card */
.card {
margin-bottom: 3px;
background: white;
border-radius: 3px;
width:398px;
float: right;
clear: both;
min-height: 100px;
padding: 10px 5px 15px 5px;
font-family:'Open Sans', Arial, sans-serif;
}
.title {
color: rgb(105, 161, 36);
font-size: 16px;
}
.states {
padding-top: 10px;
}
.state {
font-size: 12px;
color: rgb(67, 88, 98);
padding: 0px 5px 2px 5px;
clear: both;
}
.circle {
width: 10px;
height: 10px;
border-radius: 50%;
float: left;
margin: 1px 5px 5px 0px;
}
.value {
float: right;
}
.graph {
padding: 10px 5px 0px 5px;
}
.bar {
float: left;
height: 10px;
-webkit-transition: width 10s;
transition: width 10s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div class="card">
<div class="content">
<div class="graph" data-bind="foreach: allValues">
<div class="bar" data-bind="style: { background: color, width: percentage() + '%' }"/>
</div>
</div>
</div>
As the first 3 are based on object references that don't change, knockout is preserving the actual <div> that's been rendered.
For the final bar, each time allValues is evaluated, it's pushing a brand new object into the returned array. I would assume that since knockout sees that as a new object, it re-renders the div from scratch, rather than updating existing bindings.
You'll need to rework your model slightly to hold an actual object for that final value so that you can then update the observables on it in the same way.
Here's a fixed version using a static object for the "other" value:
var vm = (function generateModel() {
var data = {
name: "Sign-off",
id: "XX",
values: [{ text: "Signed-off", count: 150, color: "#5fb5cc" },
{ text: "Submitted", count: 90, color: "#75d181" },
{ text: "Not Submitted", count: 75, color: "#f8a25b" }
],
aggregates: {
count: 650
}
};
// Create a view model directly from the data which we will update
var vm = ko.mapping.fromJS(data);
// Add a computed value to calculate percentage
vm.values().forEach(function (d) {
d.percentage = ko.computed(function () {
return d.count() / vm.aggregates.count() * 100;
});
});
//Create a static "others" object
vm.other = {
text: ko.observable("Other"),
count: ko.computed(function() {
var total = vm.aggregates.count();
var count = 0;
vm.values().forEach(function(d) { count += d.count(); });
return total - count;
}),
percentage: ko.computed(function(d, b) {
var total = vm.aggregates.count();
var count = 0;
vm.values().forEach(function(d) { count += d.count(); });
return (total - count) / total * 100;
}),
color: ko.observable("#ff0000")
};
// Create a
vm.allValues = ko.computed(function() {
var values = [];
var count = 0;
var total = vm.aggregates.count();
debugger;
// Add each of these results into those that will be returned
vm.values().forEach(function(d) {
values.push(d);
count += d.count();
});
// and push static object in instead of creating a new one
values.push(vm.other);
return values;
});
return vm;
})();
ko.applyBindings(vm);
setTimeout(function() {
vm.values()[0].count(90);
vm.values()[1].count(40);
vm.values()[2].count(35);
vm.aggregates.count(3550);
}, 3000);
body {
background: rgb(40, 40, 40);
}
.spacer {
height: 230px;
}
.cards {
float: right;
}
/* Small Card */
.card {
margin-bottom: 3px;
background: white;
border-radius: 3px;
width:398px;
float: right;
clear: both;
min-height: 100px;
padding: 10px 5px 15px 5px;
font-family:'Open Sans', Arial, sans-serif;
}
.title {
color: rgb(105, 161, 36);
font-size: 16px;
}
.states {
padding-top: 10px;
}
.state {
font-size: 12px;
color: rgb(67, 88, 98);
padding: 0px 5px 2px 5px;
clear: both;
}
.circle {
width: 10px;
height: 10px;
border-radius: 50%;
float: left;
margin: 1px 5px 5px 0px;
}
.value {
float: right;
}
.graph {
padding: 10px 5px 0px 5px;
}
.bar {
float: left;
height: 10px;
-webkit-transition: width 10s;
transition: width 10s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div class="card">
<div class="content">
<div class="graph" data-bind="foreach: allValues">
<div class="bar" data-bind="style: { background: color, width: percentage() + '%' }"/>
</div>
</div>
</div>
Related
I'm a beginner developer , currently I'm working on a weather app project with vue.js . I have some problems with components and databinding with vue. Can anyone explain it to me with some code sample?!
I've read some forums, and watched some tutorial videos about custom components , tried it as well but i think I made some mistakes.
Specificly i would like to know, how can i bind my API url data to a dropdown box, that the location changes with every selection.
note: Used API is an open weather API, based on latitude and longitude locations.
var myDropdown = Vue.component('my-dropdown', {
template: '#dropdown',
data: function() {
return {
isOpen: false,
selected: null,
options: [
'Heidenheim an der Brenz',
'Giengen',
'Ulm',
]
}
},
methods: {
toggle: function() {
this.isOpen = !this.isOpen;
},
show: function() {
this.isOpen = true;
},
hide: function() {
this.isOpen = false;
},
set: function(option) {
this.selected = option;
this.hide();
}
},
mounted: function() {
console.log('My dropdown component is mounted!')
}
});
let weatherApp = new Vue({
el: '#app',
data: {
town: '',
Temp: '',
minTemp: '',
maxTemp:'',
wind: '',
description: '',
icon: '',
hdh: 'https://fcc-weather-api.glitch.me/api/current?lat=48.6833&lon=10.15',
ulm: 'https://fcc-weather-api.glitch.me/api/current?lat=48.39841&lon=9.99155',
giengen: 'https://fcc-weather-api.glitch.me/api/current?lat=48.39841&lon=9.99155'
},
methods: {
getWeather() {
var url = '';
axios
.get(url)
.then(response => {
this.town = response.data.name
this.Temp = response.data.main.temp;
this.minTemp = response.data.main.temp_min;
this.maxTemp = response.data.main.temp_max;
this.wind = response.data.wind.speed;
this.description = response.data.weather[0].description;
this.icon = response.data.weather[0].icon;
})
.catch(error => {
console.log(error);
});
},
},
beforeMount() {
this.getWeather();
},
});
body {
background: url(https://shiftyjelly.files.wordpress.com/2013/11/w.jpg?w=960&h=400);
background-repeat: no-repeat;
font-family: 'Montserrat', sans-serif;
font-weight: 100;
text-shadow: 0px 0px 2px #000000;
color: #ffffff;
width: 960px;
height: 400px;
}
#weather {
padding: 15px;
}
#temperature {
position: absolute;
font-size: 40px;
top: 240px;
left: 420px;
color: black;
}
#temp-values {
text-align: right;
position: relative;
text-justify: distribute;
display: block;
top: 60px;
left: -200px;
color: black;
}
#info {
padding: 15px;
}
#name {
top: 10px;
left: 300px;
font-size: 40px;
color: black;
position: relative;
}
.wind {
top: 180px;
left: 380px;
color: black;
position: relative;
}
#icon {
color: black;
font-size: 20px;
left: -180px;
top: 120px;
position: relative;
}
#my-dropdown {
cursor: pointer;
position: absolute;
left: 0%;
top: 0%;
min-width: 250px;
height: 40px;
}
#selected {
position: relative;
z-index: 2;
display: block;
width: 100%;
height: 40px;
padding: 0 20px;
background: rgba(05, 46, 41, 0.1);
border-radius: 10px;
font: 1.25rem/40px 'Ubuntu', Helvetica, Arial, sans-serif;
text-shadow: 2px 2px 0px #000;
color: rgb(0, 237, 255);
}
#selected: after {
opacity: 0.5;
display: inline-block;
margin-left: 10px;
content: '▼';
color: black;
}
#selected:hover: after {
opacity: 1;
}
#options {
position: absolute;
left: 0;
top: 100%;
z-index: 1;
width: 100%;
margin-top: 3px;
background: rgba(05, 46, 41, 0.1);
border-radius: 10px;
}
#option {
padding: 5px 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
font: 1.2rem 'Vollkorn', Georgia, Times, serif;
color: rgb(0, 237, 255);
text-shadow: 2px 2px 0px #000;
}
#option:hover {
background-color: rgba(0, 0, 0, 0.05);
}
#option:last-child {
border-bottom: none;
}
#fade-enter-active, .fade-leave-active {
transition: all 0.25s ease-out;
}
#fade-enter, .fade-leave-active {
opacity: 0.5;
transform: translateY(-30px);
}
* { box-sizing: border-box; }
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<title>Weather App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<template id="dropdown">
<div id="my-dropdown">
<div id="selected" #click="toggle">Select Your Town Here</div>
<div id="options" v-show="isOpen">
<div id="option" v-for="option in options" #click="set(option)">
{{ option }}
</div>
</div>
</div>
</template>
<body>
<div id="app">
<my-dropdown></my-dropdown>
<div id="weather">
<span id="name">{{town}}</span>
<span id="icon">{{description}}</span>
<span id="temperature">{{Temp}}°</span><br>
<span id="temp-values">Min: {{minTemp}}° <br> Max: {{maxTemp}}°</span>
</div>
<div id="info">
<img class="wind" height="40px" width="40px" src="https://www.svgrepo.com/show/71601/wind.svg">
<span class="wind">{{wind}} m/s</span>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>
Here is a fiddle
First, it's best to keep all of your data in one place rather than hard-coding the town names in the dropdown component. You also don't need to store the whole url each time. So remove options from the dropdown. You'll pass the town data to it via prop.
1) Restructure your app's town data into an array of objects like this:
data: {
...
towns: [
{abbr: 'hdh', name: 'Heidenheim an der Brenz', lat: '48.6833', lon: '10.15'},
{abbr: 'ulm', name: 'Ulm', lat: '48.39841', lon: '9.99155'},
{abbr: 'giengen', name: 'Giengen', lat: '48.39841', lon: '9.99155'}
]
}
2) Pass the town data via prop named "options" to the dropdown component:
<my-dropdown :options="towns"></my-dropdown>
3) Change the dropdown label to {{ option.name }}
4) Add the prop to the component:
props: ['options']
5) Emit a custom event when the town changes:
set: function(option) {
this.$emit('change-town', option);
...
}
6) Process that event in the parent template's getWeather:
<my-dropdown :options="towns" #change-town="getWeather"></my-dropdown>
7) Generate the URL and send the request:
getWeather(option) {
const urlpath = 'https://fcc-weather-api.glitch.me/api/current?'
const qs = `lat=${option.lat}&lon=${option.lon}`;
const url = path + qs;
axios.get(url)...
...
}
I have 10 links and each of them is different from the others.I want when user hovers on them background image of the div changes and a tooltip text be shown on top of the links with a fade-in animation .
i have tried to make several functions using JS and it works but it's a lot of code and mostly repetitive.I want a good shortcut through all of that useless coding.
document.getElementById("d1").onmouseover = function() {
mouseOver1()
};
document.getElementById("d2").onmouseover = function() {
mouseOver2()
};
document.getElementById("d3").onmouseover = function() {
mouseOver3()
};
document.getElementById("d1").onmouseout = function() {
mouseOut1()
};
document.getElementById("d2").onmouseout = function() {
mouseOut2()
};
document.getElementById("d3").onmouseout = function() {
mouseOut3()
};
function mouseOver1() {
document.getElementById("dogs").style.background = "blue";
document.getElementById("tooltiptext1").style.visibility = "visible";
}
function mouseOut1() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext1").style.visibility = "hidden";
}
function mouseOver2() {
document.getElementById("dogs").style.background = "green";
document.getElementById("tooltiptext2").style.visibility = "visible";
}
function mouseOut2() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext2").style.visibility = "hidden";
}
function mouseOver3() {
document.getElementById("dogs").style.background = "red";
document.getElementById("tooltiptext3").style.visibility = "visible";
}
function mouseOut3() {
document.getElementById("dogs").style.background = "black";
document.getElementById("tooltiptext3").style.visibility = "hidden";
}
#dogs {
float: right;
margin-top: 5%;
background: black;
width: 150px;
height: 150px;
}
#d-list {
color: white;
direction: ltr;
float: right;
width: 60%;
height: 60%;
}
#tooltiptext1,
#tooltiptext2,
#tooltiptext3 {
color: black;
background-color: gray;
width: 120px;
height: 30px;
border-radius: 6px;
text-align: center;
padding-top: 5px;
visibility: hidden;
}
<div id="animals">
<div id="dogs"></div>
<div id="d-list">
<pre style="font-size:22px; color:darkorange">dogs</pre><br />
<pre>white Husky</pre>
<p id="tooltiptext1">Tooltip text1</p>
<pre>black Bull</pre>
<p id="tooltiptext2">Tooltip text2</p>
<pre>brown Rex</pre>
<p id="tooltiptext3">Tooltip text3</p>
</div>
</div>
Please have in mind that all of links will change same outer div object and the idea is to change the background image of that div and the tooltip shoud appear on the top of the links....so,
any ideas?
edit: added animation requested.
CSS is almost always better done in script by using classes when multiple elements are being manipulated with similar functions so I used that here. Rather than put some complex set of logic in place I simply added data attributes for the colors - now it works for any new elements you wish to add as well.
I did find your markup to be somewhat strangely chosen and would have done it differently but that was not part of the question as stated.
I took the liberty of removing the style attribute from your dogs element and put it in the CSS also as it seemed to belong there and mixing markup and css will probably make it harder to maintain over time and puts all the style in one place.
Since you DID tag this with jQuery here is an example of that.
$(function() {
$('#d-list').on('mouseenter', 'a', function(event) {
$('#dogs').css('backgroundColor', $(this).data('colorin'));
$(this).parent().next('.tooltip').animate({
opacity: 1
});
}).on('mouseleave', 'a', function(event) {
$('#dogs').css('backgroundColor', $(this).data('colorout'));
$(this).parent().next('.tooltip').animate({
opacity: 0
});
});
});
#dogs {
float: right;
margin-top: 5%;
background: black;
width: 150px;
height: 150px;
}
#d-list {
color: white;
direction: ltr;
float: right;
width: 60%;
height: 60%;
}
.dog-header {
font-size: 22px;
color: darkorange;
margin-bottom: 2em;
}
.tooltip {
color: black;
background-color: gray;
width: 120px;
height: 30px;
border-radius: 6px;
text-align: center;
padding-top: 5px;
opacity: 0;
position:relative;
top:-4.5em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="animals">
<div id="dogs"></div>
<div id="d-list">
<pre class="dog-header">dogs</pre>
<pre>white Husky</pre>
<p id="tooltiptext1" class="tooltip">Tooltip text1</p>
<pre>black Bull</pre>
<p id="tooltiptext2" class="tooltip">Tooltip text2</p>
<pre>brown Rex</pre>
<p id="tooltiptext3" class="tooltip">Tooltip text3</p>
</div>
</div>
Updated
This answer was written before the question was edited to show the intended markup/styling and before all the details were included. The code has been updated to work with that structure.
I think the simplest thing is just to create a configuration object to detail the varying bits, and then use common code for the rest. Here's one approach:
const configs = [
['d1', 'tooltiptext1', 'blue'],
['d2', 'tooltiptext2', 'green'],
['d3', 'tooltiptext3', 'red'],
];
configs.forEach(([id, tt, color]) => {
const dogs = document.getElementById('dogs');
const el = document.getElementById(id);
const tip = document.getElementById(tt);
el.onmouseover = (evt) => {
dogs.style.background = color
tip.style.visibility = "visible";
}
el.onmouseout = (evt) => {
dogs.style.background = "black";
tip.style.visibility = "hidden";
}
})
#dogs{float:right;margin-top:5%;background:#000;width:150px;height:150px}#d-list{color:#fff;direction:ltr;float:right;width:60%;height:60%}#tooltiptext1,#tooltiptext2,#tooltiptext3{color:#000;background-color:gray;width:120px;height:30px;border-radius:6px;text-align:center;padding-top:5px;visibility:hidden}
<div id="animals"> <div id="dogs"></div><div id="d-list"> <pre style="font-size:22px; color:darkorange">dogs</pre><br/> <pre>white Husky</pre> <p id="tooltiptext1">Tooltip text1</p><pre>black Bull</pre> <p id="tooltiptext2">Tooltip text2</p><pre>brown Rex</pre> <p id="tooltiptext3">Tooltip text3</p></div></div>
Obviously you can extend this with new rows really easily. And if you want to add more varying properties, you can simply make the rows longer. If you need to add too many properties to each list, an array might become hard to read, and it might become better to switch to {id: 'demo', tt: 'dem', color: 'blue'} with the corresponding change to the parameters in the forEach callback. (That is, replacing configs.forEach(([id, tt, color]) => { with configs.forEach(({id, tt, color}) => {.) But with only three parameters, a short array seems cleaner.
Older code snippet based on my made-up markup.
const configs = [
['demo', 'dem', 'blue'],
['dd', 'dem1', 'green']
];
configs.forEach(([id1, id2, color]) => {
const a = document.getElementById(id1)
const b = document.getElementById(id2)
a.onmouseover = (evt) => {
a.style.background = color
b.style.visibility = "visible";
}
a.onmouseout = (evt) => {
a.style.background = "black";
b.style.visibility = "hidden";
}
})
div {width: 50px; height: 50px; float: left; margin: 10px; background: black; border: 1px solid #666; color: red; padding: 10px; text-align: center}
#dem , #dem1{visibility:hidden;}
<div id="demo">demo</div>
<div id="dem">dem</div>
<div id="dd">dd</div>
<div id="dem1">dem1</div>
my way of seeing that => zero Javascript:
div[data-info] {
display: inline-block;
margin:80px 20px 0 0;
border:1px solid red;
padding: 10px 20px;
position: relative;
}
div[data-bg=blue]:hover {
background-color: blue;
color: red;
}
div[data-bg=green]:hover {
background-color: green;
color: red;
}
div[data-info]:hover:after {
background: #333;
background: rgba(0, 0, 0, .8);
border-radius: 5px;
bottom: 46px;
color: #fff;
content: attr(data-info);
left: 20%;
padding: 5px 15px;
position: absolute;
z-index: 98;
min-width: 120px;
max-width: 220px;
}
div[data-info]:hover:before {
border: solid;
border-color: #333 transparent;
border-width: 6px 6px 0px 6px;
bottom: 40px;
content: "";
left: 50%;
position: absolute;
z-index: 99;
}
<div data-info="Tooltip for A Tooltip for A" data-bg="blue">with Tooltip CSS3 A</div>
<div data-info="Tooltip for B" data-bg="green" >with Tooltip CSS3 B</div>
I have tried several different approaches to place a JavaScript generated bar graph that generates its own div element into the body of the main div element without success. Does anyone know how this can be accomplished?
EDITED
Here is the CodePen to see what I am talking about. As you can see, I have a wrapper with a border around the body. However, no matter where I place the script, I cannot get it into the wrapper. It is always generated outside.
Any help will be much appreciated.
Here is the code.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Issue Tracking System"/>
<meta name="author" content="Stephen Morris">
<link rel="stylesheet" type="text/css" href="tissue.css">
</head>
<body>
<div id="wrapper">
<h2>Test</h2>
<div class="topnav">
Home
Login
</div>
<div>
<h2>Sales Subscription Dashboard</h2>
<script src="js/subscriptions_graph.js">
</div>
</div>
<div class="copyright">
Copyright © 2018
</div>
</body>
</script>
</html>
CSS
#wrapper {
margin-left: auto;
margin-right: auto;
width: 85%;
border: groove;
border-color: white;
padding: 2px;
}
#loginwrap {
margin-left: auto;
margin-right: auto;
padding: 3px;
text-align: center;
}
body {
font-family: Georgia;
padding: 10px;
background: #f1f1f1;
font-weight: bold;
}
/* top navigation bar */
.topnav {
overflow: hidden;
background-color: #333;
}
/* topnav links */
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
/* Change color on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
/* three columns next to each other */
.column1 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #aaa;
}
.column2 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #bbb;
}
.column3 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #aaa;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Card-like background for each section */
.card {
background-color: white;
padding: 30px;
margin-top: 20px;
overflow: auto;
}
/* Align about section image to right */
.aboutimg {
float: right;
}
/* Footer */
.footer {
padding: 20px;
background: #ddd;
margin-top: 20px;
}
.copyright {
text-align: center;
font-size: 10px;
padding: 5px;
}
JavaScript
function createBarChart (data) {
// Start with the container.
var chart = document.createElement("div");
// The container must have position: relative.
chart.style.position = "relative";
// The chart's height is the value of its largest
// data item plus a little margin.
var height = 0;
for (var i = 0; i < data.length; i += 1) {
height = Math.max(height, data[i].value);
}
chart.style.height = (height + 10) + "px";
// Give the chart a bottom border.
chart.style.borderBottomStyle = "solid";
chart.style.borderBottomWidth = "1px";
// Iterate through the data.
var barPosition = 0;
// We have a preset bar width for the purposes of this
// example. A full-blown chart module would make this
// customizable.
var barWidth = 25;
for (i = 0; i < data.length; i += 1) {
// Basic column setup.
var dataItem = data[i];
var bar = document.createElement("div");
bar.style.position = "absolute";
bar.style.left = barPosition + "px";
bar.style.width = barWidth + "px";
bar.style.backgroundColor = dataItem.color;
bar.style.height = dataItem.value + "px";
bar.style.borderStyle = "ridge";
bar.style.borderColor = dataItem.color;
// Visual flair with CSS Level 3 (for maximum compatibility
// we set multiple possible properties to the same value).
// Hardcoded values here just for illustration; a
// full module would allow major customizability.
bar.style.MozBoxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.WebkitBoxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.boxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.MozBorderRadiusTopleft = "8px";
bar.style.WebkitBorderTopLeftRadius = "8px";
bar.style.borderTopLeftRadius = "8px";
bar.style.MozBorderRadiusTopright = "8px";
bar.style.WebkitBorderTopRightRadius = "8px";
bar.style.borderTopRightRadius = "8px";
bar.style.backgroundImage =
"-moz-linear-gradient(" + dataItem.color + ", black)";
bar.style.backgroundImage =
"-webkit-gradient(linear, 0% 0%, 0% 100%," +
"color-stop(0, " + dataItem.color + "), color-stop(1, black))";
bar.style.backgroundImage =
"linear-gradient(" + dataItem.color + ", black)";
// Recall that positioning properties are treated *relative*
// to the corresponding sides of the containing element.
bar.style.bottom = "-1px";
chart.appendChild(bar);
// Move to the next bar. We provide an entire bar's
// width as space between columns.
barPosition += (barWidth * 2);
}
return chart;
};
window.onload = function () {
var colors = [{ color: "red", value: 40 },
{ color: "blue", value: 10 },
{ color: "green", value: 100 },
{ color: "black", value: 65 },
{ color: "yellow", value: 75 },
{ color: "purple", value: 120 },
{ color: "grey", value: 121 },
{ color: "orange", value: 175 },
{ color: "olive", value: 220 },
{ color: "maroon", value: 275 },
{ color: "brown", value: 300 },
{ color: "teal", value: 15 }
];
var chart = createBarChart(colors);
document.body.appendChild(chart);
};
Your problem is that you are appending it to the body - therefore getting the bar graph out of the box.
It will be placed into the #wrapper if you swap that this line:
document.body.appendChild(chart);
for this:
document.querySelector('#wrapper').appendChild(chart);
Note that this is best seen in the full screen mode of the snippet - you will need to address the overflow when the graph is bigger than the containing wrapper on smaller screens. I popped an overflow-x: auto style rule there to show it is within the wrapper.
Also you were not closing the script tag correctly - so i fixed that as well.
function createBarChart (data) {
// Start with the container.
var chart = document.createElement("div");
// The container must have position: relative.
chart.style.position = "relative";
// The chart's height is the value of its largest
// data item plus a little margin.
var height = 0;
for (var i = 0; i < data.length; i += 1) {
height = Math.max(height, data[i].value);
}
chart.style.height = (height + 10) + "px";
// Give the chart a bottom border.
chart.style.borderBottomStyle = "solid";
chart.style.borderBottomWidth = "1px";
// Iterate through the data.
var barPosition = 0;
// We have a preset bar width for the purposes of this
// example. A full-blown chart module would make this
// customizable.
var barWidth = 25;
for (i = 0; i < data.length; i += 1) {
// Basic column setup.
var dataItem = data[i];
var bar = document.createElement("div");
bar.style.position = "absolute";
bar.style.left = barPosition + "px";
bar.style.width = barWidth + "px";
bar.style.backgroundColor = dataItem.color;
bar.style.height = dataItem.value + "px";
bar.style.borderStyle = "ridge";
bar.style.borderColor = dataItem.color;
// Visual flair with CSS Level 3 (for maximum compatibility
// we set multiple possible properties to the same value).
// Hardcoded values here just for illustration; a
// full module would allow major customizability.
bar.style.MozBoxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.WebkitBoxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.boxShadow = "rgba(128, 128, 128, 0.75) 0px 7px 12px";
bar.style.MozBorderRadiusTopleft = "8px";
bar.style.WebkitBorderTopLeftRadius = "8px";
bar.style.borderTopLeftRadius = "8px";
bar.style.MozBorderRadiusTopright = "8px";
bar.style.WebkitBorderTopRightRadius = "8px";
bar.style.borderTopRightRadius = "8px";
bar.style.backgroundImage =
"-moz-linear-gradient(" + dataItem.color + ", black)";
bar.style.backgroundImage =
"-webkit-gradient(linear, 0% 0%, 0% 100%," +
"color-stop(0, " + dataItem.color + "), color-stop(1, black))";
bar.style.backgroundImage =
"linear-gradient(" + dataItem.color + ", black)";
// Recall that positioning properties are treated *relative*
// to the corresponding sides of the containing element.
bar.style.bottom = "-1px";
chart.appendChild(bar);
// Move to the next bar. We provide an entire bar's
// width as space between columns.
barPosition += (barWidth * 2);
}
return chart;
};
window.onload = function () {
var colors = [{ color: "red", value: 40 },
{ color: "blue", value: 10 },
{ color: "green", value: 100 },
{ color: "black", value: 65 },
{ color: "yellow", value: 75 },
{ color: "purple", value: 120 },
{ color: "grey", value: 121 },
{ color: "orange", value: 175 },
{ color: "olive", value: 220 },
{ color: "maroon", value: 275 },
{ color: "brown", value: 300 },
{ color: "teal", value: 15 }
];
var chart = createBarChart(colors);
document.querySelector("#wrapper").appendChild(chart); // I altered this line
};
#wrapper {
margin-left: auto;
margin-right: auto;
width: 100%;
border: groove;
border-color: white;
padding: 2px;
overflow-x: auto;
}
#loginwrap {
margin-left: auto;
margin-right: auto;
padding: 3px;
text-align: center;
}
body {
font-family: Georgia;
padding: 10px;
background: #f1f1f1;
font-weight: bold;
}
/* top navigation bar */
.topnav {
overflow: hidden;
background-color: #333;
}
/* topnav links */
.topnav a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
/* Change color on hover */
.topnav a:hover {
background-color: #ddd;
color: black;
}
/* three columns next to each other */
.column1 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #aaa;
}
.column2 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #bbb;
}
.column3 {
float: left;
width: 30%;
padding: 15px;
height: 300px;
text-align: center;
background-color: #aaa;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* Card-like background for each section */
.card {
background-color: white;
padding: 30px;
margin-top: 20px;
overflow: auto;
}
/* Align about section image to right */
.aboutimg {
float: right;
}
/* Footer */
.footer {
padding: 20px;
background: #ddd;
margin-top: 20px;
}
.copyright {
text-align: center;
font-size: 10px;
padding: 5px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Issue Tracking System"/>
<meta name="author" content="Stephen Morris">
<link rel="stylesheet" type="text/css" href="tissue.css">
</head>
<body>
<div id="wrapper">
<h2>Test</h2>
<div class="topnav">
Home
Login
</div>
<div>
<h2>Sales Subscription Dashboard</h2>
</div>
</div>
<div class="copyright">
Copyright © 2018
</div>
<script src="js/subscriptions_graph.js"></script>
</body>
</html>
I've created the following fiddle https://jsfiddle.net/jnoweb/v421zzbe/2/
At the moment it has one variable which makes all three IDs count up to 20:
var game = {score:0},
scoreDisplay = [
document.getElementById("score1"),
document.getElementById("score2"),
document.getElementById("score3")];
function add20() {
TweenLite.to(game, 1, {score:"+=20", roundProps:"score", onUpdate:updateHandler, ease:Linear.easeNone});
}
function updateHandler() {
scoreDisplay.forEach(function(display) {
display.innerHTML = game.score;
});
}
add20();
I want to change this so that each ID counts to a different value, for example 16, 18 and 20!
Does anyone know how to achieve this?
Here is the more elegant, generic, configurable solution.
function ScoreDisplay(id, increment) {
this.elm = document.getElementById(id);
this.inc = increment;
this.game = {score: 0};
}
ScoreDisplay.prototype = {
update: function(){
this.elm.innerHTML = this.game.score;
}
};
var scoreDisplay = [];
scoreDisplay.push(new ScoreDisplay('score1', 16));
scoreDisplay.push(new ScoreDisplay('score2', 18));
scoreDisplay.push(new ScoreDisplay('score3', 20));
function addScore() {
scoreDisplay.forEach(function(sd) {
TweenLite.to(sd.game, 1, {score: "+=" + sd.inc, roundProps:"score", onUpdate:sd.update.bind(sd), ease:Linear.easeNone});
});
}
addScore();
#score1 {
position: relative;
font-size: 30px;
font-weight: bold;
padding: 20px;
text-align: center;
background-color: transparent;
color: $white;
border-radius: 20px 20px;
top: -11px;
left: -42px;
}
#score2 {
position: relative;
font-size: 30px;
font-weight: bold;
padding: 20px;
text-align: center;
background-color: transparent;
color: $white;
border-radius: 20px 20px;
top: -11px;
left: -42px;
}
#score3 {
position: relative;
font-size: 30px;
font-weight: bold;
padding: 20px;
text-align: center;
background-color: transparent;
color: $white;
border-radius: 20px 20px;
top: -11px;
left: -42px;
}
<!--TweenLite/TweenMax GSAP-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/easing/EasePack.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenLite.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TimelineLite.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/RoundPropsPlugin.min.js"></script>
<div id="prodArrow"></div>
<div id="prodCount">
<div id="score1"></div>
</div>
<div id="testArrow"></div>
<div id="testCount">
<div id="score2"></div>
</div>
<div id="devArrow"></div>
<div id="devCount">
<div id="score3"></div>
</div>
var game = {
score1: 0,
score2: 0,
score3: 0
},
scoreDisplay = [
document.getElementById("score1"),
document.getElementById("score2"),
document.getElementById("score3")
];
function add(scoreIndex, numToAdd) {
TweenLite.to(game, 1, {score:("+="+numToAdd), roundProps: ("score" + scoreIndex), onUpdate:updateHandler, ease:Linear.easeNone});
}
function updateHandler() {
scoreDisplay.forEach(function(display, i) {
var key = ("score" + (i+1))
display.innerHTML = game[key];
});
}
add(1 , 16);
add(2 , 18);
add(3 , 20);
What about this ?
var game = {
score1:0,
score2:0,
score3:0
},
scoreDisplay = [
document.getElementById("score1"),
document.getElementById("score2"),
document.getElementById("score3")];
function add20() {
TweenLite.to(game, 1, {score1:"+=16", score2:"+=18", score3:"+=20", roundProps:"score", onUpdate:updateHandler, ease:Linear.easeNone});
}
function updateHandler() {
scoreDisplay.forEach(function(display, key) {
var score = scoreDisplay[key].id;
display.innerHTML = game[score];
});
}
add20();
https://jsfiddle.net/hundma4g/
I'm currently working on this small project that randomly displays a div (#box) of 100px width and height. I want this div to appear ONLY in another div (#boxBorder) so it appears to be limited to a specific area on the page.
Here is the content of my HTML:
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder"></div>
<div id="box"></div>
Here is the CSS:
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
background-color: red;
display: none;
border-radius: 50px;
position: relative;
z-index: 1;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT, #scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore, #cScore {
font-weight: normal;
}
h1, #directions, #scoreT, #scoreC {
width: 100%;
text-align: center;
}
And lastly, the javascript function for random position:
//Get random position
function getRandomPos() {
var pos = Math.floor((Math.random() * 500) + 1);
console.log("POS: " + pos + "px");
return pos + "px";
}
Which I call within a timeout method:
setTimeout(function() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top=getRandomPos();
document.getElementById("box").style.left=getRandomPos();
document.getElementById("box").style.backgroundColor=getRandomColor();
document.getElementById("box").style.borderRadius=getRandomShape();
document.getElementById("box").style.display="block";
}, rTime);
I'm not very skilled in positioning and I can't seem to get these two divs to align so that the #box div can recognize the size of the #boxBorder div and stay within those limits. Any help would be appreciated!
Couple things wrong here:
You need the box div nested inside the borderBox div if you want to use the relative positioning.
<div id="boxBorder">
<div id="box"></div>
</div>
The randomPos function needs to take into account the size of the box, so only multiply by 400 instead of 500.
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
return pos + "px";
}
Set the style to inline-block, not block for the box.
Use setInterval instead of setTimeout to have it repeat.
var rTime = 1000;
function getRandomPos() {
var pos = Math.floor((Math.random() * 400));
console.log("POS: " + pos + "px");
return pos + "px";
}
function getRandomColor() {
return ['#bf616a', '#d08770', '#ebcb8b', '#a3be8c', '#96b5b4', '#8fa1b3', '#b48ead'][(Math.floor(Math.random() * 7))];
}
function randomizeBox() {
createdTime = Date.now();
console.log("make box: " + createdTime);
document.getElementById("box").style.top = getRandomPos();
document.getElementById("box").style.left = getRandomPos();
document.getElementById("box").style.backgroundColor = getRandomColor();
}
setInterval(randomizeBox, rTime);
#boxBorder {
height: 500px;
width: 500px;
margin: 20px auto;
left: 0;
right: 0;
background-color: white;
border: 1px black solid;
position: absolute;
z-index: 0;
}
#box {
margin: 0 auto;
height: 100px;
width: 100px;
border-radius: 50px;
position: relative;
z-index: 1;
display: inline-block;
}
h1 {
margin: 15px 0 0 0;
}
#directions {
margin: 0;
padding: 5px;
font-size: 0.8em;
}
#scoreT,
#scoreC {
font-weight: bold;
margin: 10px 50px 0 0;
}
#tScore,
#cScore {
font-weight: normal;
}
h1,
#directions,
#scoreT,
#scoreC {
width: 100%;
text-align: center;
}
<h1>Test your reactions!</h1>
<p id="directions">Click the shape as fast as you can!</p>
<p id="scoreC">Click score: <span id="cScore">0</span>s</p>
<p id="scoreT">Total score: <span id="tScore">0</span>s</p>
<div id="boxBorder">
<div id="box"></div>
</div>