I need some help with changing bar width. I am creating a simple horizontal bar graph and need to change the width of the bar depends on the data in percent. jsfiddle link http://jsfiddle.net/kmc3ohab/2/
Thanks in advance
var data = {
"box1": {
"bar1": "80%",
"bar2": "60%",
"bar3": "40%",
"bar4": "50%",
"total": "60%",
},
};
$(document).ready(function() {
$(".score-text").html(data.box1.total);
$(".data").text(data.box1.bar1);
$(".data").text(data.box1.bar2);
$(".data").text(data.box1.bar3);
$(".data").text(data.box1.bar4);
});
body {
background: #efefef;
width: 100%;
margin: 0px;
text-align: center;
}
h2 {
font-family: 'Noto Sans', serif;
color: #b71f38;
font-weight: 300;
margin: 0px;
}
h3 {
font-family: 'Noto Sans', serif;
color: #444444;
font-weight: 200;
margin: 0px;
}
#colLeft {
width: 50%;
float: left;
}
#colRight {
width: 50%;
float: right;
}
#row {
background: #e2e2e2;
width: auto;
height: 230px;
margin: 15px;
border-radius: 5px;
}
#insideColLeft {
width: 30%;
float: left;
}
#insideColRight {
width: 69%;
float: right;
padding-top: 8px;
padding-right: 5px;
}
.circle {
margin-left: auto;
margin-right: auto;
border-radius: 50%;
width: 150px;
position: relative;
background: #b71f38;
}
.circle:before {
content: "";
display: block;
padding-top: 100%;
}
.circle-inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
text-align: center;
}
.score-text {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
height: 1em;
line-height: 1em;
font-size: 30px;
font-family: 'Fjalla One', sans-serif;
color: #ffffff;
}
.date {
font-family: 'Fjalla One', sans-serif;
text-align: center;
color: #333333;
}
ul.graph {
margin: 0;
padding: 0;
list-style-type: none;
}
ul.graph li {
margin: 10px;
height: 25px;
background: #ccc;
color: #fff;
}
ul.graph li.data {
background: #f4ebb8;
}
<div id="row">
<h2>Title</h2>
<h3>Subtitle</h3>
<div id="insideColLeft">
<div class="circle">
<div class="circle-inner">
<div class="score-text">
</div>
</div>
</div>
</div>
<div id="insideColRight">
<ul class="graph">
<li class="data">bar 1</li>
<li class="data">bar 2</li>
<li class="data">bar 3</li>
<li class="data">bar 4</li>
</ul>
</div>
</div>
What if i have more than 1 data sets ? how do i do a loop to go through every data? Also if i need to load the data from a local csv file. js fiddle updated http://jsfiddle.net/kmc3ohab/6/
Try this: http://jsfiddle.net/kmc3ohab/5/
$(document).ready(function() {
$(".score-text").html(data.box1.total);
$(".data").each(function( index, value ) {
width = eval('data.box1.bar' + (index+1));
value.innerText = width;
value.style.width = width;
});
});
Basic answer to your question was setting style.width on each bar element. Expanded on your solution to also:
doing everything in a loop. Note: Would be better if the bar values were stored in an array so you wouldn't need to use eval().
properly setting both the text on the bar and the width of the bar in the loop.
Edit for new question about multiple sections of bars.
Displaying multiple sections really depends on how you are reading the csv file. Ideally, the data is stored in arrays and looks something like this after it is read:
var data =
[
{
title: "Title 1",
subTitle: "SubTitle 1",
bars :
[
{ name: "bar1", value: "80%" },
{ name: "bar2", value: "40%" },
{ name: "bar3", value: "50%" },
{ name: "bar4", value: "60%" }
],
total: "60%"
},
{
title: "Title 2",
subTitle: "SubTitle 2",
bars :
[
{ name: "bar1", value: "80%" },
{ name: "bar2", value: "60%" },
{ name: "bar3", value: "40%" },
{ name: "bar4", value: "50%" }
],
total: "80%"
}
];
And you loop over the list of sections like this:
data.foreach(function(item) {
...
});
But the real questions is how are you going to generate the HTML? With document.write()? If the amount of data being read is dynamic and you are creating HTML on the fly, it will be easier to set the properties as you are creating the HTML.
AngularJS would be a better solution here using a ng-repeat. Then you only define the HTML for your sections once. You talk about reading a local csv file. Does that mean this is not a server hosted HTML file? Even so, it is still possible to use Angular with CDNs hosting the files for you.
Related
I created a grid of items using this library and it works flawlessly. In my items, i have a very simple bootstrap grid with some forms, inputs and other content, the problem is that a lot of times while using the content inside of the bootstrap grid, i move the entire item.
I was wondering if there is a way to disable dragging only inside of the so that when i use what's inside of the container, the item is not moved, but if i want to move the item i can drag it by grabbing it outside the container.
Here is my code:
<template>
<grid-layout :layout.sync="layout"
:col-num="1"
:row-height="30"
:is-draggable="draggable"
:is-resizable="resizable"
:vertical-compact="true"
:use-css-transforms="true"
>
<grid-item v-for="item in layout"
:static="item.static"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
<div class="container">
<div class="row">
<div class="col-sm-7">Here is some content...</div>
<div class="col-sm-5">Here is some other content.. </div>
</div>
</div>
</grid-item>
</grid-layout>
</template>
<script>
import { GridLayout, GridItem } from "vue-grid-layout"
export default {
components: {
GridLayout,
GridItem
},
data() {
return {
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", static: false},
],
draggable: true,
resizable: true,
index: 0
}
},
methods: {
itemTitle(item) {
let result = item.i;
if (item.static) {
result += " - Static";
}
return result;
}
}
}
</script>
<style scoped>
.vue-grid-layout {
background: #eee;
}
.vue-grid-item:not(.vue-grid-placeholder) {
background: #ccc;
border: 1px solid black;
}
.vue-grid-item .resizing {
opacity: 0.9;
}
.vue-grid-item .static {
background: #cce;
}
.vue-grid-item .text {
font-size: 24px;
text-align: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 100%;
width: 100%;
}
.vue-grid-item .no-drag {
height: 100%;
width: 100%;
}
.vue-grid-item .minMax {
font-size: 12px;
}
.vue-grid-item .add {
cursor: pointer;
}
.vue-draggable-handle {
position: absolute;
width: 20px;
height: 20px;
top: 0;
left: 0;
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") no-repeat;
background-position: bottom right;
padding: 0 8px 8px 0;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: pointer;
}
</style>
So basically i should not be able to drag or move the item from inside the container. Any kind of advice is appreciated. Thanks in advance!
Since you asked you probably already solved your problem, but the easy way to avoid moving the entire item is to declare an attribute of grid-item where dragging is not allowed.
<grid-item class=""
drag-allow-from=".vue-draggable-handle"
drag-ignore-from=".no-drag"
:autoSize="true" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i">
<div class="vue-draggable-handle"></div>
<div class="no-drag"></div>
</grid-item>
Thoses two attributes are in fact css class reference.
.vue-grid-item .no-drag {height: 100%;width: 100%;}
.vue-draggable-handle { position: initial; width: 5px;height: 5px;top: 0;right: 0;padding: 0 8px 8px 0;background-origin: content-box;box-sizing: border-box;border-radius: 10px;cursor: pointer;color: #005B60 !important;}
You can either set static to true which would not allow you to drag/resize the grid or add an element draggable in your layout and set it to false where you will be able to only resize the grid.
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", static: true},
] // If you want to lock the grid (no resize/no dragging)
or
layout: [
{"x":0,"y":0,"w":6,"h":4,"i":"0", draggable: false},
] // you will be able to resize but drag
<grid-item
:key="item.i"
v-for="item in layout"
:is-draggable="item.draggable"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
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)...
...
}
Before I ask my questions, let me show you the full code for the project I'm working on (I'm making a bot that answers questions given some key words. It's a very easy code developed by Deni Spasovski).
This is the main HTML file:
<html>
<head>
<script type="text/javascript" src="chat.js"></script>
<style>
* {
box-sizing: border-box;
}
body {
background-color: #edeff2;
font-family: "Calibri", "Roboto", sans-serif;
}
.chat_window {
position: absolute;
width: calc(100% - 20px);
max-width: 800px;
height: 500px;
border-radius: 10px;
background-color: #fff;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
background-color: #ffffff;
overflow: hidden;
}
.top_menu {
background-color: #fff;
width: 100%;
padding: 20px 0 15px;
box-shadow: 0 1px 30px rgba(0, 0, 0, 0.1);
}
.top_menu .buttons {
margin: 3px 0 0 20px;
position: absolute;
}
.top_menu .buttons .button {
width: 16px;
height: 16px;
border-radius: 50%;
display: inline-block;
margin-right: 10px;
position: relative;
}
.top_menu .buttons .button.close {
background-color: #f5886e;
}
.top_menu .buttons .button.minimize {
background-color: #fdbf68;
}
.top_menu .buttons .button.maximize {
background-color: #a3d063;
}
.top_menu .title {
text-align: center;
color: #bcbdc0;
font-size: 20px;
}
.messages {
position: relative;
list-style: none;
padding: 20px 10px 0 10px;
margin: 0;
height: 347px;
overflow: scroll;
}
.messages .message {
clear: both;
overflow: hidden;
margin-bottom: 20px;
transition: all 0.5s linear;
opacity: 0;
}
.messages .message.right .text {
color: #45829b;
}
.messages .message.appeared {
opacity: 1;
}
.messages .message .avatar {
width: 60px;
height: 60px;
border-radius: 50%;
display: inline-block;
}
.messages .message .text_wrapper {
display: inline-block;
padding: 20px;
border-radius: 6px;
width: calc(100% - 85px);
min-width: 100px;
position: relative;
}
.messages .message .text_wrapper::after, .messages .message .text_wrapper:before {
top: 18px;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.messages .message .text_wrapper::after {
border-width: 13px;
margin-top: 0px;
}
.messages .message .text_wrapper::before {
border-width: 15px;
margin-top: -2px;
}
.messages .message .text_wrapper .text {
font-size: 18px;
font-weight: 300;
}
.bottom_wrapper {
position: relative;
width: 100%;
background-color: #fff;
padding: 20px 20px;
position: absolute;
bottom: 0;
}
.bottom_wrapper .message_input_wrapper {
display: inline-block;
height: 50px;
border-radius: 5px;
border: 1px solid #bcbdc0;
width: calc(100% - 160px);
position: relative;
padding: 0 20px;
word-wrap: break-word;
word-break: break-word;
-webkit-hyphens: auto;
-ms-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
.bottom_wrapper .message_input_wrapper .message_input {
border: none;
height: 100%;
box-sizing: border-box;
width: calc(100% - 40px);
position: absolute;
outline-width: 0;
outline:none;
color: gray;
}
.bottom_wrapper .send_message {
width: 140px;
height: 50px;
display: inline-block;
border-radius: 7px;
background-color: #a3d063;
border: 2px solid #a3d063;
color: #fff;
cursor: pointer;
transition: all 0.2s linear;
text-align: center;
float: right;
}
.bottom_wrapper .send_message:hover {
color: #a3d063;
background-color: #fff;
}
.bottom_wrapper .send_message .text {
font-size: 18px;
font-weight: 300;
display: inline-block;
line-height: 48px;
}
.message_template {
display: none;
}
</style>
<style type="text/css">
.answerbot-input
{
color: #1AA1E1;
}
.answerbot-ai
{
color: #CE5043;
}
</style>
</head><body>
<div class="chat_window"><div class="top_menu"><div class="buttons"><div class="button close"></div><div class="button minimize"></div><div class="button maximize"></div></div><div class="title">Chat</div></div>
<div class="bottom_wrapper clearfix">
<div class="subcontent" id='subcontent'>
<p class='answerbot-ai'>
Don't be afraid, talk to me.
</p>
</div>
<div class="message_input_wrapper">
<input class="message_input" placeholder="Type your message here..." onkeyup="keypressInput(this, event);"/>
</div><div class="send_message">
<div class="icon"></div><div class="text">Send</div></div></div></div>
</li></div>
<script type="text/javascript" src="scripts.js"></script>
<script type="text/javascript" src="data.js"></script>
<script type="text/javascript">
var _answerBot = new answerBot();
function keypressInput(sender, event) {
if (event.which == 13) {
document.getElementById('subcontent').innerHTML += _answerBot.processInput(sender.value);
sender.value = '';
}
}
</script>
</body></html>
This is the scripts.js file:
var answerBot = function () {
var _this = this;
_this.processInput = function (text) {
updateUrl(text);
var _result = "<p class='answerbot-input'>" + text + "</p>";
text = text.replace(new RegExp("[ ]{2,}", "g"), " ");
var _words = text.toLowerCase().split(" ");
var _answers = [];
var _title = "";
if (_words.length === 0 || _words.toString() === '') { //if the input is empty
_answers = _this.specialContext.emptyInput;
_title = _this.specialContext.emptyInput;
} else {
var _possibleAnswers = findMatches(_words);
if (_possibleAnswers.length === 0) { //if no answer found
_answers = _this.specialContext.wrongInput;
_title = _this.specialContext.wrongInput;
}
if (_possibleAnswers.length == 1) { //context recognized
_answers = _this.answers[_possibleAnswers[0]].values;
_title = _this.answers[_possibleAnswers[0]].description;
}
if (_possibleAnswers.length > 1) {
_result += formatText(_this.specialContext.rephrase, _this.specialContext.rephrase);
for (var i = 0; i < _possibleAnswers.length; i++) {
_result += formatText(_this.answers[_possibleAnswers[i]].description, _this.answers[_possibleAnswers[i]].description);
}
}
}
if (_answers.length > 0) {
var _rand = Math.floor((Math.random() - 0.001) * _answers.length);
_result += formatText(_answers[_rand], _title);
}
return _result;
};
function formatText(text, title) {
return "<p class=\'answerbot-ai\' title=\'" + title + "\'>" + text + "</p>";
}
function findMatches(words) {
var foundKeywords = [];
var _possibleAnswers = [];
for (var i = 0; i < _this.keywords.length; i++) {
foundKeywords[i] = 0;
for (var j = 0; j < words.length; j++) {
if (_this.keywords[i].keys.indexOf(words[j]) >= 0) {
foundKeywords[i]++;
if (foundKeywords[i] == _this.keywords[i].keys.length) {
return [_this.keywords[i].value];
}
}
}
if (foundKeywords[i] * 2 > _this.keywords[i].keys.length) {
_possibleAnswers.push(_this.keywords[i].value);
}
}
return _possibleAnswers.filter(function (elem, pos) {
return _possibleAnswers.indexOf(elem) == pos;
});
}
function updateUrl(text){
history.pushState(null, null, "#question=" + encodeURIComponent(text));
if(typeof ga === "function")//google analytics
ga('send', 'event', 'question', text);
}
};
And this is the data.js file:
if (answerBot) {
answerBot.prototype.specialContext = {
"wrongInput": ["I don't understand you.", "Could you rephrase the question?"],
"emptyInput": ["Please say something", "Speak louder", "Well i can't read minds."],
"rephrase": ["Can you tell me if your question was about one of the following things:"]
};
answerBot.prototype.keywords = [
{ "keys": ["hi"], "value": 0 },
{ "keys": ["hello"], "value": 0 },
{ "keys": ["life", "universe", "everything"], "value": 1 },
{ "keys": ["software", "development"], "value": 2 },
{ "keys": ["software", "engineering"], "value": 2 },
{ "keys": ["who", "made", "you"], "value": 3 },
{ "keys": ["who", "wrote", "you"], "value": 3 },
{ "keys": ["who", "coded", "you"], "value": 3 },
{ "keys": ["is", "this", "real", "life"], "value": 4 },
{ "keys": ["who", "is", "deni"], "value": 5 },
{ "keys": ["tell", "me", "about", "deni"], "value": 5 },
{ "keys": ["tell", "me", "about", "author"], "value": 5 },
{ "keys": ["show", "me", "author"], "value": 5 },
{ "keys": ["can", "see", "source"], "value": 6 },
{ "keys": ["can", "see", "sourcecode"], "value": 6 },
{ "keys": ["show", "me", "code"], "value": 6 },
{ "keys": ["how", "are", "you"], "value": 7 },
{ "keys": ["who", "is", "this"], "value": 8 }];
answerBot.prototype.answers = [
{
"description": "Hi!",
"values": ["Hello there!", "Hi how can I help you today", "Hi! What brings you here?"]
},
{
"description": "What is the answer to life the universe and everything?",
"values": ["42"]
},
{
"description": "What is software development?",
"values": ["Programming! Do you speak it?"]
},
{
"description": "Who created me?",
"values": ["I was created by another <a href='http://about.me/deni' target='_blank'>bot</a>.", "The question is who sent you here?"]
},
{
"description": "Is this real life?",
"values": ["No this is the internetz!", "Find out <a href='http://www.youtube.com/watch?v=txqiwrbYGrs' target='_blank'>yourself</a>!"]
},
{
"description": "Who is Deni?",
"values": ["This is his <a href='https://plus.google.com/+DeniSpasovski/' target='_blank'>G+ profile</a>.", "This is his <a href='www.linkedin.com/in/denispasovski' target='_blank'>Linkedin profile</a>."]
},
{
"description": "Where is your source?",
"values": ["Here is the <a href='https://github.com/denimf/Answer-bot' target='_blank'>source</a>."]
},
{
"description": "How are you?",
"values": ["I'm good how are you?"]
},
{
"description": "Who is this?",
"values": ["StackOverflow Exception occurred", "The question is who are you?"]
}
];
}
As you can see, the chat works very well answering questions and I'm looking forward to implementing it with the questions I want to be answered. However, I'm having some issues that, despite how much I try, I can't seem to fix! (I don't know that much about coding, I apologize in advance!)
I can't fix the title on top. If you write enough, it will end up disappearing because new messages push it out of the chat window.
I can't seem to allow scrolling to see past messages... I've tried with overflow scroll and it doesn't work... The messages are fixed and I can't move up the timeline
I can't get the Send button to work either! The chat only works with the Enter key.
Thank you very much!!
$( document ).on( "vclick", '#icon', function() {
$("#navigation").animate({height: 'toggle'}, 600, "linear");
});
Hello,
I currently have a hamburger menu that works perfectly except for one thing. If I click the #icon 10 times, it will execute the code 10 times, I wonder if its possible to make it only clickable when its finished and remove the whole queue system.
Thanks in advance.
You could probably do something in the lines of this:
JavaScript
var animating = false;
$(document).on( "vclick", '#icon', function() {
if(!animating) {
animating = true;
$("#navigation").animate({height: 'toggle'}, 600, "linear", function() {
animating = false;
});
}
});
You could use a workaround by hidding the #icon until the animation done then show it again :
$( document ).on( "vclick", '#icon', function() {
var _this = $(this);
//Hide the icon
$(this).hide();
$("#navigation").animate({height: 'toggle'}, 600, "linear", function() {
//Show the icon after animation
_this.show();
});
});
Hope this helps.
Using Arg0n's answer as inspiration, I have created a jQuery plugin.
The plugin used closure which means that the function returns another function so that variables are scoped inside, rather than outside; in the global scope.
(function($) {
$.fn.collapser = function(targetSelector, ms, type) {
this.on('click', function() {
var animating = false;
return (function() {
if (!animating) {
animating = true;
$(targetSelector).animate({
height: 'toggle'
}, ms, type, function() {
animating = false;
});
}
}());
});
}
}(jQuery))
$('#icon').collapser('#navigation', 600, 'linear');
body {
background: #222;
}
#page {
background: #DDD;
}
#icon {
width: 4em;
height: 4em;
line-height: 4em;
text-align: center;
margin-bottom: 1em;
background: #446;
color: #FFF;
font-weight: bold;
cursor: pointer;
border: thin solid #AAD;
}
ul#navigation {
list-style-type: none;
margin: 0;
padding: 0.25em;
background: #E7E7F7;
}
ul#navigation li {
display: inline-block;
width: 6em;
height: 2.5em;
line-height: 2.5em;
margin: 0;
padding: 0;
text-align: center;
background: #F7F7FF;
}
#content {
background: #FFF;
padding: 1em;
height: 50vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page">
<div id="icon">Icon</div>
<ul id="navigation">
<li>Home</li>
<li>Articles</li>
<li>Historic</li>
<li>About</li>
<li>Contact</li>
</ul>
<div id="content">
Hello World
</div>
</div>
You could also unbind and re-bind the event. This way you don't have to create any state variables. This may be the best way to handle this.
(function($) {
$.fn.collapser = function(targetSelector, ms, type) {
var eventName = 'click';
var fn = function(source) {
$(source).unbind(eventName, fn);
$(targetSelector).animate({
height: 'toggle'
}, ms, type, function() {
$(source).bind(eventName, fn);
});
};
this.on(eventName, function() {
fn.call(this);
});
}
}(jQuery))
$('#icon').collapser('#navigation', 1600, 'linear');
body {
background: #222;
}
#page {
background: #DDD;
}
#icon {
width: 4em;
height: 4em;
line-height: 4em;
text-align: center;
margin-bottom: 1em;
background: #446;
color: #FFF;
font-weight: bold;
cursor: pointer;
border: thin solid #AAD;
}
ul#navigation {
list-style-type: none;
margin: 0;
padding: 0.25em;
background: #E7E7F7;
}
ul#navigation li {
display: inline-block;
width: 6em;
height: 2.5em;
line-height: 2.5em;
margin: 0;
padding: 0;
text-align: center;
background: #F7F7FF;
}
#content {
background: #FFF;
padding: 1em;
height: 50vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page">
<div id="icon">Icon</div>
<ul id="navigation">
<li>Home</li>
<li>Articles</li>
<li>Historic</li>
<li>About</li>
<li>Contact</li>
</ul>
<div id="content">
Hello World
</div>
</div>
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>