I have looked around, but found no solution.
I want to print out the 'interpolation' points between this:
.card.ani25 {
bottom: 72%;
left:85%;
}
and this:
.card.ani49 {
bottom: 46%;
left: 46%;
}
Basically, I am trying to update all numerical values in that CSS at the same time. Updating one value at a time works, but is of no use in this case.
What I have tried so far, well, everything coming to mind, including triggering some infinite loops, but this is what is "stable":-)
for (var i = 25; i < 50; i++){
for (var j = 72; j >=46; j-=2){
for (var k = 85; k >=46; k-=3){
t.innerHTML = '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
}
}
This only prints out the final iteration, as can be seen here:
http://codepen.io/damianocel/pen/ZLEXOR
If I put a += after the "innerHTML2, it blows up, screen freezes, just like an infinite loop.
I know, the outermost loop runs first, then the next inner loop second multiple times, but I have never seen a case with 3 variables. Honestly, I wouldn't know the solution for 2 variables only either.
Thanks and very sorry if this has been asked before.
The problem is is that the operation for changing HTML through reassigning innerHTML is slow. When you use += it will rewrite the HTML each time, that's what's causing it to slow down. That said it still works, a faster way to do this is use a variable to hold the string and update that variable. Then at the end assign the value of t.innerHTML to that string:
var t = document.getElementById('target');
var str = "";
for (var i = 25; i < 50; i++){
for (var j = 72; j >=46; j-=2){
for (var k = 85; k >=46; k-=3){
str += '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
}
}
t.innerHTML = str;
Edit
After clarification it appears you only want a single loop and update the variables each time in that loop. In that case you can do something like:
var t = document.getElementById('target');
var str = "";
for (var i = 25, j = 72, k = 85; i < 50 && j >=46 && k >=46; i++, j-=2, k-=3){
str += '.card.ani' + i + ' {' + '<br>' + 'bottom: ' + j + '%;' + '<br>' + 'left: ' + k + '%;' + '<br>' + '}';
}
t.innerHTML = str;
For each section inside a for loop for(x;y;z) you can use a comma to make many statements. So in the first section you can define 3 variables and in the last section update 3 variables. The middle section runs the loop as long as all of those conditions are met, when a single one fails it stops.
Note: You can use || (OR) if you would like it to keep going as long as one of the conditions are true.
Code Pen (single loop)
Code Pen
I'd promote a very different approach.
I think of this as a number of distinct ranges that need to be animated over in parallel. So I would like to have a function that knows how to do just that.
const rangeFns = (...configs) => fn => val =>
fn.apply(null, configs.map(cfg => (cfg.end - cfg.start) * val + cfg.start))
This takes a collection of range descriptions and returns a function that accepts a custom function with one parameter for each of those ranges and finally returns a simple function that accepts a number between 0 and 1, returning the result of your custom function with the interpolation for each range being supplied for the respective parameter. That's a bit of a mouthful, but I think it's easy to use:
const anim = rangeFns(
{start: 25, end: 49},
{start: 72, end: 46},
{start: 85, end: 46}
)((ani, bottom, left) => `
.card.ani${Math.round(ani)} {
bottom: ${bottom}%;
left: ${left};
}`
);
If you ran this with values 0, 0.25, 0.5, 0.75, and 1, you'd get these results:
.card.ani25 {
bottom: 72%;
left: 85;
}
.card.ani31 {
bottom: 65.5%;
left: 75.25;
}
.card.ani37 {
bottom: 59%;
left: 65.5;
}
.card.ani43 {
bottom: 52.5%;
left: 55.75;
}
.card.ani49 {
bottom: 46%;
left: 46;
}
(Obviously you could add rounding to bottom and left if that's desired.)
You could then use this in an animation, by passing values calculated from your start and finish times.
To my mind the advantage is that this is more explicit. It makes the ranges more declarative, and makes the function that uses the results a lot more straightforward.
A version of this is available on Codepen.
Update
A slightly better version of this would be to use arrays rather than objects. (I originally had additional parameters in there that turned out unnecessary. I didn't notice that removing them allowed for a nice clean-up.)
const multiRange = (...ranges) => fn => val =>
fn.apply(null, ranges.map(range => (range[1] - range[0]) * val + range[0]))
const anim = multiRange([25, 49], [72, 46], [85, 46])((ani, bottom, left) => `
.card.ani${Math.round(ani)} {
bottom: ${bottom}%;
left: ${left};
}`
);
I think this reads better.
This version is also on Codepen.
Related
I try to create a system replacement for ToolTip.
I already create a version but its not quite optimal (search a better way to do it)
here's a fiddle : http://jsfiddle.net/forX/Lwgrug24/
I create a dictionary (array[key]->value). the array is order by length of the key.
each key is a word or an expression, the value is the definition of the expression.
So, I replace the expression by a span (could/should be a div). The span is used for the tooltip (I use the data-title as tooltip text).
because some word is reused in expression, I need to remove expression already with tooltip (in real life think of father/grandfather, you dont want the definition of father inside grandfather). For replacement I use a ramdom value. That's the worst of this code.
You could make comment or post a new way to do it. maybe someone already did it.
Clarification :
I think my way to do it is wrong by using a string for replacement. Or it could be more secure. How should I do it?
html :
<div class="container">
one two three four five six seven eight nine ten
</div>
javascript :
$(function() {
var list = [
{'k':'one two three four five','v':'First five number.'},
{'k':'four five six seven','v':'middle number.'},
{'k':'six seven eight','v':'second middle number.'},
{'k':'two','v':'number two.'},
{'k':'six','v':'number six.'},
{'k':'ten','v':'number ten.'}
];
$(".container").each(function(){
var replacement = new Array();
for (i = 0; i < list.length; i++) {
var val = list[i];
var rString = randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
replacement[rString + "_k"] = htmlEncode(val["k"]);
replacement[rString + "_v"] = htmlEncode(val["v"]);
var re = new RegExp("(" + val["k"] + ")","g");
$(":contains('" + val["k"] + "')",$(this).parent()).html(function(_, html) {
var newItem = '<span class="itemWithDescription" '
+ 'data-title="' + rString + "_v" + '">'
+ rString + "_k"
+ '</span>';
return html.replace(re, newItem);
});
}
for (var k in replacement){
$(this).html($(this).html().replace(k,replacement[k]));
console.log("Key is " + k + ", value is : " + replacement[k]);
}
});
$(document).tooltip({
items:'.itemWithDescription',
tooltipClass:'Tip',
content: function(){
var title = $(this).attr("data-title");
if (title == ""){
title = $(this).attr("title"); //custom tooltips
}
return title;
}
});
});
function randomString(length, chars) {
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
return result;
}
function htmlEncode(value){
//create a in-memory div, set it's inner text(which jQuery automatically encodes)
//then grab the encoded contents back out. The div never exists on the page.
return $('<div/>').text(value).html();
}
I added a little thing. on the random function, I put a | and } for every char, its bigger but there's not much chance to have a conflic with an expression.
for (var i = length; i > 0; --i) result += '|' + ( chars[Math.round(Math.random() * (chars.length - 1))] ) + '}' ;
http://jsfiddle.net/forX/Lwgrug24/3/
As the title suggests, I am trying to create a function that can build me a css animation.
I accept 3 parameters in the function:
Name = what you will call the animation
steps = the steps the animation should take (ie: ["0%","50%","100%"])
stepProperties = the css properties to be added into the step
I want to then take those parameters and "match" them with one another (steps[0] and stepProperties[0] should go together, etc)
Then I want to push to conjoined and matched parameter into a new array called keyframeProperties, so for example:
createAnimation("testAnimation", ["0%", "25%", "50%", "75%", "100%"], ["background: blue","background: red","background: yellow","background: purple","background: green"]);
I should expect that after the loop, keyframeProperties is now equal to:
keyframeProperties["0%{background: blue}","25%{background: red}","50%{background: yellow}","75%{background: purple}","100%{background: green}"]
Here is my current code:
var createAnimation = function(name, steps, stepProperties) {
//set up a new array which will hold our properties to insert into the animation
var keyframeProperties = [];
//loop over the steps and the step properties and push them into the keyframeProperties array in their right format
for (var i = 0; i < steps.length; i++) {
for (var j = 0; j < stepProperties.length; j++) {
keyframeProperties.push(steps[i] + "{" + stepProperties[j] + "}");
}
}
var animation = '#keyframes ' + name + '{' +
keyframeProperties +
'}';
alert(animation);
}
I hope all that made sense!
--SD
Your were close to solve this issue on your own.
I just removed nested for loops and done some other small modifications, please ask if something is not clear.
var createAnimation = function(name, steps, stepProperties) {
//set up a new array which will hold our properties to insert into the animation
var keyframeProperties = [];
//loop over the steps and the step properties and push them into the keyframeProperties array in their right format
for (var i = 0; i < steps.length; i++) {
keyframeProperties.push(steps[i] + "{" + stepProperties[i] + "}");
}
var animation = '#keyframes ' + name + '{' + keyframeProperties.toString().replace(/,/g, ' ') + '}';
return animation;
}
var animationString = createAnimation("testAnimation", ["0%", "25%", "50%", "75%", "100%"], ["background: blue","background: red","background: yellow","background: purple","background: green"]);
document.getElementById("result").innerHTML = animationString;
<div id="result"></div>
Here is another take on the function and it has a slightly different API. The routine takes three parameters: 1) animation name, 2) object of properties to be animated and properties' values, and 3) [optional] animation steps.
Examples:
A. One or more animated properties
buildAnimation("blink", {"opacity": [0, 1]});
buildAnimation("fade", {"opacity": [1, 0.5, 0], "color": ["#aaa", "#ccc", "#eee"]});
B. Specified steps (to be used for irregular animations)
buildAnimation("blink", {"opacity": [1, 0, 1, 0]}, [0, 50, 75, 100]);
Fiddle: http://jsfiddle.net/wxdpr9we/.
And a source code:
function arrayRange(from, to, steps) {
var increment = (to - from) / (steps - 1);
for(var i = 0, arr = []; i < steps; arr.push(Math.round(i * increment)), i++);
return arr;
}
function buildAnimation(name, properties, steps) {
var propertyNames = Object.keys(properties);
var first = propertyNames[0];
var length = properties[first].length;
var animation = "";
if(typeof steps !== "undefined" && (!Array.isArray(steps) || length !== steps.length)) {
throw new Error("steps and values array lengths must be equal");
} else if(typeof steps === "undefined") {
steps = arrayRange(0, 100, length);
}
animation = "#keyframes " + name + "{";
steps.forEach(function(step, stepIndex) {
animation += step + "%{";
propertyNames.forEach(function(property) {
animation += property + ":" + properties[property][stepIndex] + ";";
});
animation += "}";
});
return animation += "}";
}
Basically i'm creating a script to display the place value for set of numbers. Here's my script:
var arrn = '3252';
var temp = 0;
var q = arrn.length;
var j = 0;
for (var i = q-1; i >= 0; i--,j++) {
if (j!=0) temp = temp + ' + ';
{
temp += arrn[i] * Math.pow(10, j);
}
}
alert(temp);
My goal is to achieve 3000 + 200 + 50 + 2. But i get 2 + 50 + 200 + 3000. I tried temp.reverse() & sort functions but doesn't seem to work. Please help
Change
if(j!=0)temp=temp +' + ';
{
temp +=arrn[i]*Math.pow(10,j);
}
to
if(j!=0) {
temp=' + ' + temp;
}
temp = arrn[i]*Math.pow(10,j) + temp;
Live Example
Side note: Your braces in the first code block above are very misleading. What you have:
if(j!=0)temp=temp +' + ';
{
temp +=arrn[i]*Math.pow(10,j);
}
is
if(j!=0)temp=temp +' + ';
temp +=arrn[i]*Math.pow(10,j);
which is to say
if(j!=0) {
temp=temp +' + ';
}
temp +=arrn[i]*Math.pow(10,j);
the block in your version is not associated with the if, it's just a freestanding block.
Side note #2: Since you're using temp as a string everywhere else, I would initialize it with '' rather than with 0. Example The reason your string didn't end up with an extraneous 0 was really quite obscure. :-)
Just add the number to the beginning of the string instead of at the end:
for (var i = q - 1; i >= 0; i--, j++) {
if (j != 0) {
temp = ' + ' + temp;
}
temp = arrn[i] * Math.pow(10, j) + temp;
}
Demo: http://jsfiddle.net/Guffa/rh9oso3f/
Side note: You are using some confusing brackets in your code after the if statement. As there is a statement following the if statement, the brackets starting on the next line becomes just a code block, but it's easy to think that it's supposed to be the code that is executed when the condition in the if statement is true.
Another side note: the language attribute for the script tag was deprecated many years ago. Use type="text/javascript" if you want to specify the language.
How about temp.split("+").reverse().join(" + ")?
You can do this way. I know it can be optimised. But it works
var arrn='3252';
var temp=0;
var q=arrn.length;
var res = [];
var j=0;
for(var i=q-1;i>=0;i--,j++)
{
temp += parseFloat(arrn[i])*Math.pow(10,j);
res.push(arrn[i]*Math.pow(10,j));
}
res.reverse();
alert(res.join('+') + " = " + temp);
http://jsfiddle.net/he7p8y5m/
var arrn='3252';
var temp=new Array();
var q=arrn.length;
for(var i=0;i<=q-1; i++){
temp.push(arrn[i]*Math.pow(10,(q-i-1)));
}
temp = temp.join('+');
alert(temp);
I'm reading a book about JS, and I'm stuck with one of the excercises.
I wrote a for-in to display the browser properties.
for (var i in navigator) {
document.getElementById('divResult').innerHTML += i + ': ' + navigator[i] + '<br />';
};
Now is the question how can I display only the first 10 of the navigator properties?
Thnx for helping me out!
You're using an object, and unlike an array objects don't have a "length" property that lets you easily run a loop over them. There are lots of ways to do this, but the easiest way to understand when you're learning is to create a "counter" variable outside expression and check to see if its true:
var counter = 0;
for (var i in navigator)
{
if ( counter <= 10 )
{
document.getElementById('divResult').innerHTML += i + ': ' + navigator[i] + '<br />';
counter = counter + 1;
}
};
There are other ways to cause the expression to stop, as mentioned above, but this (for me) was the easiest way to think about things when I started
Increment a counter, quit the loop when it hits 10
var i = 0;
for (var k in navigator) {
i++;
document.getElementById('divResult').innerHTML += i + ', ' + k + ': ' + navigator[k] + '<br />';
if (i === 10)
break;
};
I'm trying to increment a margin-left value with each passing of a loop. How is this achieved?
Here's the JSFiddle and here's what I'm trying to do:
var myObj = {
"dog":"pony",
"sass":"tude",
"war":"peace"
};
for (i in myObj) {
$('#mainDiv').append("<p>" + i + "</p>");
$('p').css("marginLeft","++20px");
}
How to have each <p> tag incremented by 20px more than the <p> tag before it?
Change
$('p').css("marginLeft","++20px");
to
$('p').each(function() {
var thisp = $(this);
thisp.css("marginLeft", thisp.css("marginLeft") + 20);
});
You can use a variable outside of the loop to keep track of something incrementing. You also weren't properly getting the right p tag in your loop, if I'm understanding you correctly.
var myObj = {
"dog":"pony",
"sass":"tude",
"war":"peace"
};
var lastMargin = 0;
for (i in myObj) {
$('#mainDiv').append("<p class='"+i+"'>" + i + "</p>");
lastMargin += 5;
$("."+i).css("marginLeft",lastMargin+"px");
}
You obviously might want to use :nth-child with the i variable instead of classes.
Create a var that keep the count.
Also, only aim for the last p (last one added):
var myObj = {
"dog":"pony",
"sass":"tude",
"war":"peace"
},
count = 0;
for (i in myObj) {
$('#mainDiv').append("<p>" + i + "</p>");
$('p').last().css("marginLeft",(20 * count) + 'px');
count++
}
Fiddle : http://jsfiddle.net/3hxDK/4/
It seems like you're trying to have each subsequent paragraph indented more than the last?
If so, you need to reference the paragraph on its own.
var count = 0;
for (i in myObj) {
$("<p>" + i + "</p>").
css('marginLeft', count + "px").
appendTo($('#mainDiv'));
count += 20;
}