progressive input placeholder - javascript

I'm curious how to implement progressive/dynamic placeholder like the below example in CSS/Javascript.
PS. I know we need to show some kind of work/effort before posting to SO. but I'm kinda confuse what to search to get relevant information

Set the <input placeholder="" attribute using setTimeout or setInterval for an animation loop.
A simpler version just spams a bunch of future updates using setTimeout instead of an animation loop as I think it's simpler - though this approach does not scale.
var finalPlaceholderText = "foo bar baz";
var input = document.getElementById( 'idOfTextBox' );
var placeholderIndex = 0;
for( var i = 0; i < finalPlaceholderText.length; i++ ) {
setTimeout(
function( length ) {
input.placeholder = finalPlaceholderText.substring( 0, i );
}
, i * 500, i );
}
Note you need to pass length (i) as a parameter into the setTimeout callback because JavaScript's closures will use the last value after the for loop finishes instead of the value used when setTimeout was called each time.
Using a setTimeout-loop, it would look like this (note the lack of a for loop):
var finalPlaceholderText = "foo bar baz";
var input = document.getElementById( 'idOfTextBox' );
function incrementPlaceholderText( i ) {
input.placeholder = finalPlaceholderText.substring( 0, i );
if( i < finalPlaceholderText.length ) {
setTimeout( incrementPlaceholderText, 500, i + 1 );
}
}
incrementPlaceholderText( 0 );
Or more generally (in a way that supports multiple input elements and different placeholder texts):
function incrementPlaceholderText( input, finalText, i ) {
input.placeholder = finalText.substring( 0, i );
if( i < finalText.length ) {
setTimeout( incrementPlaceholderText, 500, input, finalText, i + 1 );
}
}
incrementPlaceholderText( document.getElementById( 'idOfTextBox1' ), "foo bar baz", 0 );
incrementPlaceholderText( document.getElementById( 'idOfTextBox2' ), "foo bar baz qux", 0 );

You could use setInterval function:
var finalPlaceholderText = "foo bar bazaazz";
var input = document.getElementById('idOfTextBox');
var len = finalPlaceholderText.length;
var timerID;
var counter = 0;
if (counter <= len) {
timerID = setInterval(function() {
counter = counter + 1;
typewriter(counter)
}, 100);
}
function typewriter(i) {
input.placeholder = finalPlaceholderText.substring(0, i);
if (i === len) {
counter = 0;
//comment out below if you want it to stop
//clearInterval(timerID)
}
}
<input id="idOfTextBox" placeholder="" />

Hopefully this snippet will be useful. Have added comments for clarification
//create a variable. This will be used to create substring
var initialChar = 0;
//get the element and placeholder
let getElement = document.getElementById('inputElem');
let getPlaceHolderText = getElement.getAttribute('placeholder');
//create IIFE and this will be called as long as placeholder is not
//completly created
(function setPlaceholder() {
//settime out function to input one text at a time
let clearNow = setTimeout(function() {
// increase the count
initialChar++;
//create a substring and set this value as placeholder
let getChar = getPlaceHolderText.substring(0, initialChar);
getElement.setAttribute('placeholder', getChar + '|')
// when the variable value and length of string
// is equal it mean all the placeholder text has been created
// if not equal then add next character to placeholder
if (initialChar !== getPlaceHolderText.length) {
// calling the IIFE
setPlaceholder()
} else {
// equal so remove the pipe(pipe to create cursor effect)
getElement.setAttribute('placeholder', getElement.getAttribute('placeholder').slice(0, -1));
clearTimeout(clearNow);
}
}, Math.ceil(Math.random() * 150)) // any random number
}())
<input type="text" placeholder="Add you text here" id="inputElem">

After inspecting the sources found out that there's an awesome library already built for this.
https://github.com/chinchang/superplaceholder.js

Related

JS - my loop using setTimeout stops looping

I have the following code :
var i = 0, count = 10, randomId;
function f(myArray) {
// this will get a random value from myArray
randomId = myArray[Math.floor( Math.random()*myArray.length )];
// displays the random value
alert (randomId);
i++;
if( i < count ){
setTimeout( f, 3000 );
}
}
f(myArray);
The above code works but gives only one alert and then it stops.
However it works properly (10 loops) with basic alerts such as alert("hi"), and remove the randomId line.
It's as if anything complex within this function will block the loop, it will only handle basic alerts..
Any help is appreciated, thanks :)
In your setTimeout you are not passing the array:
Try this:
if( i < count ){
setTimeout(() => f(myArray), 3000 );
}
^ that creates a lambda function so that you can pass a value to your callback in the timeout.
var i = 0, count = 10, randomId;
function f(myArray) {
// this will get a random value from myArray
randomId = myArray[Math.floor( Math.random()*myArray.length )];
// displays the random value
alert (randomId);
i++;
if( i < count ){
setTimeout(() => f(myArray), 3000 );
}
}
f([1,2,3,4,5,6,7,8]);

Closure to set element classes in Javascript with setTimeout

I am trying to animate through CSS properties by adding "start" and "end" classNames through javascript. I have defined a bunch of these classes in CSS (for instance starting with opacity: 0 and ending with opacity: 1
I am trying to loop through all of the elements, set a start className on an element, then an end className on the element to trigger the transition. The problem is if I do that how I normally would, by the time the function finishes there would be no actual className change.
Using setTimeout with even no delay is one way to get around this, as was mentioned here but that method does not work in this instance because of my looping. I am using a closure.
Here is my JS code:
var animation = (function () {
var a = {};
a.divIndex = -1;
a.imgIndex = startHolders.length;
a.animIndex = -1;
a.divs = startHolders;
a.run = function () {
if (++a.divIndex === a.divs.length) a.divIndex = 0;
if (++a.animIndex === animations.length) a.animIndex = 0;
if (++a.imgIndex === imageElements.length) a.imgIndex = 0;
imageElements[a.imgIndex].className = animations[a.animIndex]['start'];
setTimeout(function() {
imageElements[a.imgIndex].className = animations[a.animIndex]['end'];
}, 0);
startHolders[a.divIndex].appendChild(imageElements[a.imgIndex]);
};
setInterval(a.run, 1000);
return a;
})();
What I really wanted was to be able to set all those indexes to 0 and use this instead of a (just some placeholder object) but I couldn't do this with the setTimeout because of how it changes the value of this. Another problem here is that if I put the if(++a.index) at the bottom of the code, by the time setTimeout runs the value ofa.index` has changed (I believe) Does anybody know of a workaround here or just a better way?
If you would like access to this inside of the function passed to setTimeout you can use bind.
For example:
var obj = {}
obj.divIndex = "do you wanna build a snowman?";
obj.run = function () {
console.log(this.divIndex);
}
setTimeout(obj.run.bind(obj), 1000);
I'm thinking you might want to do something like this?:
var a = {};
a.divIndex = -1;
a.imgIndex = startHolders.length;
a.animIndex = -1;
a.divs = startHolders;
a.run = function () {
if (++this.divIndex === this.divs.length) this.divIndex = 0;
if (++this.animIndex === animations.length) this.animIndex = 0;
if (++this.imgIndex === imageElements.length) this.imgIndex = 0;
imageElements[this.imgIndex].className = animations[this.animIndex]['start'];
setTimeout(function() {
imageElements[this.imgIndex].className = animations[this.animIndex]['end'];
}.bind(this), 0);
startHolders[this.divIndex].appendChild(imageElements[this.imgIndex]);
};
setInterval(a.run.bind(a), 1000);

Arrays: not to lose elements inside a synchronized function?

I would like to know why, in the following code, the elements of coupleFound array are deleted when the function setInterval dies. The idea is, get into the a element and check her tag name "ai".
When he accept this tag he makes a copy of the entire element starting from his parent, and put into the coupleFound array. It works, but only inside of set Interval function! I don't understand because I declared the array outside of the function! I believe this is happening because "set Interval" is not synchronized, but I don't know how fix this problem!
var clicked = 0,
totalClicks = 3,
index = 0,
listIds = new Array("289657", "2680235", "1597254", "269621"),
coupleFound = new Array( ),
videos = document.getElementById( "videos_list" );
var interval = setInterval(function( ) {
coupleList = videos.getElementsByTagName( "a" );
for(var i = coupleList.length; i--;) {
for(j=0; j < listIds.length; j++) {
if(coupleList[i].getAttribute( "ai" ) == listIds[j]) {
coupleFound[index] = coupleList[i].parentNode;
index++;
break;
}
}
videos.removeChild( videos.lastChild );
}
document.getElementById('btnMoreVideos').click();
clicked++;
if(clicked >= totalClicks) {
clearInterval( interval );
alert("I'm inside of the function. The length is:" + coupleFound.length)
}
}, 1000);
alert("The length of the array is:" + coupleFound.length);
What can I do to solve this problem?
You're more or less right. setInterval is asynchronous. Your last line of code will be ran immediately, before setInterval fires for the 3rd time.
There's no good way to sleep/block in JavaScript. You have to restructure your code so that it runs in the order you want. Normally this means using callbacks. i.e., call a function from inside your if(clicked >= totalClicks) block that does what you want, rather than putting it after setInterval.
e.g.,
var clicked = 0,
totalClicks = 3,
index = 0,
listIds = new Array("289657", "2680235", "1597254", "269621"),
coupleFound = new Array( ),
videos = document.getElementById( "videos_list" );
function allDone() {
alert("The length of the array is:" + coupleFound.length);
}
var interval = setInterval(function( ) {
coupleList = videos.getElementsByTagName( "a" );
for(var i = coupleList.length; i--;) {
for(j=0; j < listIds.length; j++) {
if(coupleList[i].getAttribute( "ai" ) == listIds[j]) {
coupleFound[index] = coupleList[i].parentNode;
index++;
break;
}
}
videos.removeChild( videos.lastChild );
}
document.getElementById('btnMoreVideos').click();
clicked++;
if(clicked >= totalClicks) {
clearInterval( interval );
alert("I'm inside of the function. The length is:" + coupleFound.length);
allDone();
}
}, 1000);
Although you should avoid global variables as much as possible. Maybe pass coupleFound or its length into allDone instead if you don't need access to too many vars.

javascript / jquery - finding the closest value in an array

I'm modifying the jquery ui slider. I have certain "stops" I want the user to be able to slide to, expressed as a percentage of the slider's overall width. So, for example, if I have 3 stops, they will be distributed evenly at 0, 50, and 100 (%). I store these in an array [0,50,100].
When the user drags the slider and releases, I capture the slider's current value. So if he scrolled 56% of the way across the bar, his stopVal is 56.
How do I write a function that will then determine which number in the array this stopVal is closest to? Here's my code:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
}
This function will let you do that:
Array.prototype.closest = function(value) {
var i;
function diff(n) {
var diff = n - value;
return diff < 0 ? -diff : diff;
}
var found = this[0],
mindiff = diff(found);
for (i = 1 ; i < this.length ; i++) {
var currentdiff = diff(this[i]);
if (currentdiff < mindiff) {
found = this[i];
mindiff = diff(found);
}
}
return found;
}
Now you can do this:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
stopVal = optValArr.closest(stopVal);
}
NOTE: Some people consider defining prototypes for native types as dangerous as it can cause conflicts if two libraries do the same (just like global variable). If you are writing a public library you should therefore avoid adding to the prototypes of native types.
Try This:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var diff=101;
var val =0;
for(var i =0; i < optValArr.length; i++){
var tmpDiff = Math.abs(stopVal - optValArr[i]);
if(tmpDiff < diff){
diff=tmpDiff;
val = optValArr[i]
}
}
}
slideStop("something", {"value":20});
Demo: http://jsfiddle.net/ggzZj/
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var closestVal = optValArr.reduce(function (memo, curr) {
var currDiff = Math.abs(curr - stopVal),
memoDiff = Math.abs(memo - stopVal)
return memoDiff < currDiff ? memoDiff : currDif
})
}
Another common definition of "closer" is based on the square of the difference. But, you could do that by simply adding the number that you want in your original array like this:
[10,40,50, my_number]
Then, sort your array and then you choice if you want the closest position from the right or the left.
What do you think?

Why is this Javascript much *slower* than its jQuery equivalent?

I have a HTML list of about 500 items and a "filter" box above it. I started by using jQuery to filter the list when I typed a letter (timing code added later):
$('#filter').keyup( function() {
var jqStart = (new Date).getTime();
var search = $(this).val().toLowerCase();
var $list = $('ul.ablist > li');
$list.each( function() {
if ( $(this).text().toLowerCase().indexOf(search) === -1 )
$(this).hide();
else
$(this).show();
} );
console.log('Time: ' + ((new Date).getTime() - jqStart));
} );
However, there was a couple of seconds delay after typing each letter (particularly the first letter). So I thought it may be slightly quicker if I used plain Javascript (I read recently that jQuery's each function is particularly slow). Here's my JS equivalent:
document.getElementById('filter').addEventListener( 'keyup', function () {
var jsStart = (new Date).getTime();
var search = this.value.toLowerCase();
var list = document.querySelectorAll('ul.ablist > li');
for ( var i = 0; i < list.length; i++ )
{
if ( list[i].innerText.toLowerCase().indexOf(search) === -1 )
list[i].style.display = 'none';
else
list[i].style.display = 'block';
}
console.log('Time: ' + ((new Date).getTime() - jsStart));
}, false );
To my surprise however, the plain Javascript is up to 10 times slower than the jQuery equivalent. The jQuery version takes around 2-3 seconds to filter on each letter, while the Javascript version takes 17+ seconds! I'm using Google Chrome on Ubuntu Linux.
This isn't for anything really important so it doesn't need to be super efficient. But am I doing something really dumb with my Javascript here?
You could try using textContent instead of innerText , I think it should be faster. Also timing the list-generation and loop separately would tell if there is problem in list-generation.
Another best practice for javascript speed is caching the list.length in a variable and calling the variable like:
l = list.length;
for (var i=0;i<l;i++):{ code here}
And maybe timing with jsperf would be better.
Here, I've refactored your code a bit:
var filter = document.getElementById( 'filter' ),
ablist = document.querySelector( '.ablist' );
filter.addEventListener( 'keyup', function () {
var re, elems, i, len, elem;
re = RegExp( this.value, 'i' );
elems = ablist.children;
for ( i = 0, len = elems.length; i < len; i += 1 ) {
elem = elems[i];
elem.style.display =
elem.textContent.search( re ) > -1 ? 'list-item' : 'none';
}
}, false );
Live demo: http://jsfiddle.net/MVFxn/
Changes:
with a regular expression and an i flag, there's no need for toLowerCase,
if there is only one '.ablist' element on the page, querySelector should be the fastest way to grab it (since it aborts the query once it finds the first such element),
there's no query for the LI elements since the children property already references them conveniently.
I'd love to know how this code performs on your page...
I used while instead of for and did some minor improvements. Here is the final code.
var list = list = document.querySelectorAll('ul.ablist > li');
document.getElementById('javascriptFilter').addEventListener( 'keyup', function () {
var jsStart = (new Date).getTime(),
search = this.value.toLowerCase(),
i = list.length - 1,
listItem,
result;
while( i >= 0 )
{
listItem = list[i];
if ( listItem.textContent.toLowerCase().indexOf(search) === -1 )
listItem.style.display = 'none';
else
listItem.style.display = 'block';
i--;
}
result = ((new Date).getTime() - jsStart);
console.log(['Time: ', result, '<br />'].join(''));
}, false );

Categories