D3 scale fails if first value is different than zero - css

In this simple example: http://jsfiddle.net/2VeGY/1/
<!doctype html>
<meta charset="utf-8">
<title>single column</title>
<style>
*{margin:0,padding:0}
input{width:800px;}
nav{border:1px solid gray; width:850px;}
li{display:inline-block; height:30px; }
li:hover{opacity:0.8}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<input value="[{"v":0,"c":"red"},{"v":100,"c":"#005"},{"v":200,"c":"#12d"}, {"v&quo\
t;:300,"c":"#1dd"}, {"v":400,"c":"red"} ]">
<nav>
<ul>
</ul>
</nav>
<script>
var wscale = d3.scale.linear().range(["0px","800px"])
update()
d3.select("input").on("change",update)
function update(){
var data = JSON.parse(d3.select("input").property("value"));
var li=d3.select("ul").selectAll("li").data(data);
wscale.domain(d3.extent(data,function(d){return d.v}))
li.enter().append("li")
li
.style("width",function(d,i){
start=d.v
i+1 == data.length ? end=d.v : end=data[i+1].v;
return wscale(end-start)
})
.style("background-image",function(d,i){
start=d.c;
i+1 == data.length ? end=d.c : end=data[i+1].c;
return "linear-gradient(to right, "+start+","+end+")"});
li.exit().remove()
}
</script>
you can change the color scale of the steps data[...].c and their position data[...].v.
The scale is updated dynamically.
My problem is the following: Why it messes up if the first value is different than zero?
Thanks a lot for your help!

You're seeing this behaviour because with your current code, you always pass 0 as an input value to wscale. For the last li element, end is going to be the same as start in your function to set the width and therefore what you're passing to wscale will be 0. The input domain of wscale is determined as the extent of the input values and doesn't take this additional value into account.
You can easily fix this by changing how the domain is determined:
wscale.domain([0, d3.max(data,function(d){return d.v})]);
This assumes that all of your v values are positive.
However, what you really want to do is take into account the differences between the values, as that's what you're passing to the scale. That is, the total sum of differences should be equal to the maximum width. You can compute this as follows.
var sumdiff = 0;
for(var i = 0; i < data.length - 1; i++) {
sumdiff += data[i+1].v - data[i].v;
}
Then the scale becomes
wscale.domain([0, sumdiff]);
Complete example here.

you shouldn't put the quotation marks in the range, it should be like so:
.range([0,800])

Related

Creating a rating system for displaying rating stars in asp.net

I've a values something like this 1.25, 2.50, 3.75 or 4.00:
I can loop on the integers like 1, 2, 3 or 4.
But how I can loop on 3.75?
for (int i = 0; i < 3.75; i++)
{
// my logic
}
Updated:
The loop I needed as I'm creating a rating system and displaying the rating stars in loop. For example:
If 1.25 then star 1 and quarter (0.25) of star 2. Or if 4.75 then star 4 and last quarter (0.75) of star.
3.75/5
How I can display rating in stars?
Prepare 2 image files, one has 5 empty-stars and the other has 5 filled-stars, just like:
the 2 images must have same width and height, e.g. 400X70
place the filled-stars overlap on the empty-stars, then crop the filled-stars to a portion of the origin witdh the same as the Rating,
i.e. the crop div width = image_width / 5 * rating
e.g. With 3.5 Rating, width = 400 / 5 * 3.5 = 280
<html>
<head><title>test</title>
<style type="text/css">
.container > * {
position: absolute;
}
.container, .crop {
height: 70px;
}
.crop {
overflow: hidden;
}
</style>
</head>
<body>
<div class="container">
<img src="https://i.stack.imgur.com/yiT2y.png" />
<!-- the width could be calculated either at server or client side, or define in css -->
<div class="crop" style="width:280px">
<img src="https://i.stack.imgur.com/oTi9e.png" />
</div>
</div>
</body>
</html>
Although generally it's not a good idea to loop on double, especially when you check for equality in the loop, in your case it's OK because your loop increment is can be represented precisely as a combination of powers of 2. Specifically, 1.25 is 20+2-2.
for (double i = 0 ; i < 6 ; i+=1.25) {
// my logic
}
Alternatively, you could loop on int, and multiply loop counter by 1.25:
for (int i = 1 ; i <= 4 ; i++) {
double val = 1.25 * i;
//
}
I'm creating a rating system and displaying the rating stars in loop.
You don't need a loop for this. If you have a number between 0 and 5, inclusive, representing the average, you need to know three things:
How many "filled" stars to display,
How big is the filled portion of of the partially-filled star, and
How many "blank" stars to display.
You can find out the answers to these three questions using math:
The number of "filled" stars is the integer portion of the number after truncation
The fraction of the partially filled star is the decimal part of the number
The number of "blank" stars is 5-ceil(n), where ceil(n) represents the "ceiling" of the number (i.e. the smallest int equal or higher than n).
Just use loop variable of type double:
for (double x = 1.25; x <= 4; x += 1.25)
{
// Your logic
}
You can iterate over a list of accepted values instead of iterating on float. If you don't want to manually edit known values, one way to generate them would be:
IEnumerable<float> CalculateBreakpoints(int min, int max, short unitPartitions)
{
var fraction = 1f / unitPartitions;
for (float i = min; i <= max; i += fraction)
{
yield return i;
}
}
It's unlikely that in the wild you'll have ratings that exactly match your known values. You could try choosing known value closest to rating at hand or approach it some other way. Amazon for example shows closest value above actual rating. That could be done like this:
float MapRatingToBreakpoint(float rating, IEnumerable<float> breakpoints)
{
var min = breakpoints.Min();
var max = breakpoints.Max();
if (rating < min || rating > max)
{
throw new ArgumentOutOfRangeException(nameof(rating));
}
foreach (var point in breakpoints)
{
if (rating <= point)
{
rating = point;
break;
}
}
return rating;
}
Once you have a processed rating, to display stars on the front end this code would be able to choose what type of star to use:
for (let i = 1; i <= 5; ++i) {
if (i <= Math.floor(rating)) {
console.log("addFullStar()");
}
else if (i > Math.ceil(rating)) {
console.log("addEmptyStar()");
}
else {
fraction = rating - i + 1;
console.log(`addPartialStar(fraction: ${fraction}`);
}
}
At this point it's up to you to choose how you want to display each type of star. You could use a sprite with all the different stars and play with CSS classes like <span class="star-0-25" /> to attach correct images. Another option for displaying a partial star could be to add full and empty star on top of each other, have empty star with hidden overflow, full star with visible overflow and adjust width of a parent tag.

Add classes to adjacent elements dynamically on mouseenter

I'm creating a grid of elements and have a hover effect in place, using CSS transitions, for each element. I'd like to add secondary effects on adjacent x- and y-axis elements as well, creating a cloud effect. I imagine I'll be referencing those elements using jQuery's next() and prev() methods, or by $index and $parent.$index.
The grid area will be large enough to prevent row-wrapping (using negative margins and hidden overflow).
Here's a simplified example of my repeat:
<div class="activity-thumb-row" ng-repeat="i in getNumArray(20) track by $index">
<div class="activity-thumb"
ng-class="{'adjacent': adjacent}"
ng-repeat="j in getNumArray(30) track by $index"
ng-mouseenter="highlightActivities()">
</div>
</div>
And a function in the controller (which I realize may not be the best approach):
$scope.highlightActivities = function() {
$(this).next().adjacent = true;
$(this).prev().adjacent = true;
}
How can I target elements adjacent to the hovered element using ng-class (or something else) inside ng-repeat?
Here's a fiddle for fiddling.
For reference, here are some related discussions:
Change class on mouseover in directive
Angular js ng repeat with conditional ng class not applying css class
ng-mouseover and leave to toggle item using mouse in angularjs
Here's a directive that calculates all of the indices of adjacent cells and adds the adjacent class using jQuery only ... not ng-class.
Assumes that rows will wrap , would need adjusting for individual row elements
.directive('activityThumb', function() {
return {
restrict: 'C',
link: function(scope, elem) {
elem.bind('mouseenter', function(e) {
var elW = elem.width(),
$parent =elem.parent(),
parentW = $parent.width(),
$items = $parent.children(),
numItems =$items.length
itemsPerRow = Math.floor(parentW / elW),
idx = elem.index(),
rowIndex = idx % itemsPerRow;
/* object of various indices , easy to inspect*/
var adjacentIdx = {
top: idx > itemsPerRow ? idx - itemsPerRow : false,
right: rowIndex != itemsPerRow ? idx + 1 : false,
left: rowIndex > 0 ? idx - 1 : false,
bottom: (numItems - idx) > itemsPerRow ? idx + itemsPerRow : false
}
console.dir(adjacentIdx);
$items.removeClass('adjacent')
$.each(adjacentIdx, function(position, index){
if(index !== false){
$items.eq(index).addClass('adjacent');
}
});
});
}
}
});
It wouldn't take much tweaking to remove jQuery dependency either.
Also would need additional directive on parent to remove extra classes when mouse leaves the main parent from one of the edges
DEMO
First, it's not a good idea to deal with DOM elements in the controller.
Also, this problem seems to be mostly styling related, and not functionality related. I would thus try to keep the logic in the View and not in the controller.
There are 2 ways to deal with View-specific logic: 1) using custom directives or 2) View-defined scope variables
The second approach can work here and seems like the cheapest approach, but also a bit ugly. It ng-inits the rowHighlight array in the scope and sets which element is highlighted:
<div ng-repeat="i in getNumArray(20) track by $index" ng-init="rowHighlight = []">
<div class="activity-thumb"
ng-repeat="j in getNumArray(30) track by $index"
ng-class="{'adjacent': rowHighlight[$index-1] || rowHighlight[$index+1]}"
ng-mouseenter="rowHighlight[$index] = true"
ng-mouseleave="rowHighlight[$index] = false">
</div>
</div>
updated fiddle

Many floating DIVs same height [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I have such situation: http://jsfiddle.net/HKHS3/
The question is how to make divs appear row after row where all divs in one row have the same height, depending on actual content of the tallest one?
So, depending on body's width the number of divs in a row will vary but each time the div right after the end of row should kind of clear floating and start a new row.
Fixed number per row
You can do this by creating a row type div to wrap your inner div elements.
First you need to restructure your HTML, something like this:
<div class="row">
<div>abc</div>
<div>adb djhf kdfhv fkjsh vhf jhds fjhf jh fjhf jh fdjh dh</div>
<div>dhfjgh jfh gkjhfde jghf jgh jfdh gjfhd gjfdhg jfhd gjdhf jhg djhg jdh gjhfd</div>
</div>
(you can add more rows like this as you need to)
Then the following css should do what you need:
.row {
display:table-row;
}
.row > div {
width: 100px;
display:inline-block;
vertical-align:top;
border: 1px solid black;
margin: 5px;
height:100%;
}
Here is your example updated
Dynamic number per row (not perfect)
The problem with the above method is that it requires you to have a fixed number of div elements per row. If you wanted it to be dynamic and wrap then you will have a problem doing this with just CSS alone. The closest you could get to it would be as follows:
div {
width: 100px;
display:inline-block;
vertical-align:top;
margin: 5px;
}
But the elements do not all have the same height, it is just you cannot tell that without the border. Due to this, adding a border, background-color or any other style that shows the element's height will break the effect.
Here is an example of this
Exactly as required (requires javascript)
It is worth mentioning that the effect you want is doable using javascript. I wont include an example of this because the actually implementation will depend heavily on how your real HTML is set up.
Actually, I had a quick go at the javascript approach, it uses JQuery though, and can likely be optimised too:
function updateHeights() {
var maxHeight = 0, lastY = 0, rowDivs = [], allDivs = $("div"), count = allDivs.length;
allDivs.each(function (i) {
var div = $(this), offset = div.offset(), y = offset.top, x = offset.left, h = div.height();
if (h > maxHeight) maxHeight = h;//store the highest value for this row so far
if (lastY == 0) lastY = y;//get the y position if this is the first element
//if new row
if (y > lastY) {
resizeElements(rowDivs, maxHeight);//resize all elements on this row
rowDivs.length = 0;//reset the array of row elements, ready for next row
maxHeight = h;//set maxHeight to first of new row
}
lastY = y;//store current y posible for checking if we have a new row or not
rowDivs.push(div);//add current element to row collection
//check if last item, is so then resize this last row
if(count - 1 == i)
resizeElements(rowDivs, maxHeight);
});
}
function resizeElements(elements, height) {
for (var i = 0; i < elements.length; i++) {
$(elements[i]).height(height);
}
}
$(window).resize(function () {
updateHeights();
});
updateHeights();
Here is a working example
It is very simple using jQuery. For all divs in same row give single class. Lets say 'sameheight' in my example. Then use this jQuery.
$(document).ready(function(){
var maxHeight = 0;
$(".sameheight").each(function(){
if ($(this).height() > maxHeight) { maxHeight = $(this).height(); }
});
$(".sameheight").height(maxHeight);
});
For multiple rows repeat the code with different class.
Hope this solves your issue.
try this
<script type="text/javascript">
$(document).ready(function () {
var maxHeight = 0;
$('div').each(function () {
var this_div = $(this);
if (maxHeight < this_div.height()) {
maxHeight = this_div.height();
}
})
$('div').css({ 'height': maxHeight.toString() });
})
</script>

css inline-block div positioning

I'm trying to fix a positing issue in a responsive design.
I have a container div, containing 4 (but it could be more or less) divs that are displayed as inline-block. I would like to know how to control the number of divs per line when the page is resized (with CSS, if it's possible). For example, when 4 containees no longer fits in the container, the last one is moved to second line. I would like in that case to have 2 containees in the first line and 2 containees in the second line. I dont know how to do that. Your help would be welcomed!
EDIT:
it could also be 6 containees, in the case the layout would be:
- 1 line of 6 blocks if it fits
- 2 lines of 3 blocks
- 3 lines of 2 blocks
- 6 lines of one
the number of containees is variable. I just want to have the same number of containees per line
the html:
<div class="container">
<div class="containee"></div>
<div class="containee"></div>
<div class="containee"></div>
<div class="containee"></div>
</div>
the css:
.containee {
width:200px;
height:200px;
display:inline-block;
background-color:tomato
}
the example can be seen here : http://cssdesk.com/uGLbq
(PS : I tried to find the solution searching the web but I dont really know the good keywords related with this topic)
You can't with CSS (AFAIK).
You can do "the math" dynamically with Javascript in real time.
In your case,
you known the width of one block (in that moment),
you can easily get the window width (in that moment),
you know the number of your block (in that moment);
Simply apply ( (1) the first time you open the page, and (2) every time the number of blocks changes, or (3) the resolution changes) the algorithm in the following code:
// EXAMPLE OF INPUT
var windowWidth = 1400; // read it...
var blockWidth = 200; // read it or use const...
var numberOfBlocks = 10; // read it...
// Calculate the maximum number of blocks per row
var maxBlocksPerRow;
for (var i=0; i < numberOfBlocks; i++) {
if ( (blockWidth * (i + 1)) > windowWidth){
maxBlocksPerRow = i;
break;
}
}
// Check the highest 0 module combination while iterating backwards
var magicNumberForMatchingBlocks = 1; // if not found, it will be 1.
for (var i = maxBlocksPerRow; i > 0 ; i--) {
if ( (numberOfBlocks % i) == 0){
magicNumberForMatchingBlocks = i;
break;
}
}
alert("With " + numberOfBlocks + " blocks, each one wide " +
blockWidth + " pixels, and a window wide " + windowWidth + " pixels,
the number of blocks per row for having always
the same number of block in any row is: " + magicNumberForMatchingBlocks);
Then use that number to populate or re-arrange the elements with Javascript or better with some Javascript library like jQuery.
html:
<div class="container">
<div class="grouped">
<div class="containee"></div>
<div class="containee"></div>
</div>
<div class="grouped">
<div class="containee"></div>
<div class="containee"></div>
</div>
</div>
css:
.containee {
width:200px;
height:200px;
display:inline-block;
background-color:tomato
}
.grouped {
float:left;
}
Try this:
.container
{
min-width: 410px;
}
Give the .containee a float:left; if the page fits for 4, they will be positioned right beside each other, else, you'll have another line of divs. You can give it as well a margin-top:5px; in case you got another line, the divs of the second line won't be glued to the divs of the first line. Note that with this approach, its not obliged to have equal number of .containee in each line, if you have 4, then you re-size, you'll have 3 - 1, then 2 - 2...etc..

CSS3 webkitAnimationEnd event ordering

I'm new to Stack Overflow and also relatively new to HTML5 programming. I'm writing something (for Safari, primarily) where the logic is driven by the events which get fired out when webkit animations complete. If I start a number of animations of the same length simultaneously, I need some idea of the order I can expect their completion events to fire. Example:
<!DOCTYPE html>
<html>
<head>
<style>
#-webkit-keyframes slideRight {
from { left: 0; }
to { left: 100px; }
}
</style>
</head>
<body>
<script type="text/javascript">
var square = function(yPos, color){
var myDiv = document.createElement("div");
myDiv.style.width = "20px";
myDiv.style.height = "20px";
myDiv.style.top = yPos + "px";
myDiv.style.backgroundColor = color;
myDiv.style.position = "absolute";
document.body.appendChild(myDiv);
var squareInterface = {
onAnimEnd: function(event){
console.log(myDiv.style.backgroundColor + " square finished animating");
},
startAnim: function(){
myDiv.style.webkitAnimationName = "slideRight";
myDiv.style.webkitAnimationDuration = "2s";
myDiv.addEventListener('webkitAnimationEnd', this.onAnimEnd);
}
}
return squareInterface;
}
var myRedFoo = square(0, "red");
var myBlueFoo = square(30, "blue");
myRedFoo.startAnim();
myBlueFoo.startAnim();
</script>
</body>
</html>
So, I'm creating a red square and a blue square in JavaScript, and (in Safari and Chrome) kicking off animations to move them to the right, and to print to the console when they're done. The blue square is always the first to say that it's finished animating. From playing around it seems to have nothing to do with the order in which the animations were started, or the positions of the squares, but the order in which they're created. "Simultaneous" event callbacks seem to occur on the most recently created element first, followed by the older elements.
My question is can I rely on this behaviour? Is it guaranteed in any standards, or is it likely to change depending on the browser, or the phase of the moon? If the event order can't be guaranteed, what strategies would you recommend for coping with that?
I can say that this is probably system dependent. I'm using OSX Lion, and in both Chrome and Safari the "red" event is logged before the "blue" one.
If you want to hack it out so that you can be more confident in the timings, do something as such:
function startRedFoo(){ myRedFoo.startAnim() };
myBlueFoo.startAnim();
setTimeout(startRedFoo, 10); //Ten is as small as you can go.
You would think that you would be able to set the timeout function to myRedFoo.startAnim but that prevents the messages from being logged.
I can still imagine potential timing issues with this though, so it's not fool proof.

Resources