jQuery 2.1.0 | Display elements incrementally - javascript

I need to display rows of buttons incrementally from top to bottom in a scrollable parent div. These rows of buttons are created using a replace method applied onto a text string converted to html. Since I can have 500+ more rows of buttons to create and display, the text-to-html conversion can take a few seconds or longer, which freezes the UI during conversion. The code below uses a setInterval which unlocks the UI and provides a cool way of "animating" the gradual display of button rows. Problem is, currently, the whole set of rows is repeated at each setInterval, that's not what I want, I can't figure out what to do next. I need each individual row to be displayed incrementally without repeating, from top to bottom, in the order provided by the string, until the length of array is met. The scrolable parent div is a fixed height of 300px. Maybe a Lazy loading method would be better? Any help solving this issue is appreciated.
DEMO fiddle
var placeholder = $('#placeholder');
placeholder.html(placeholdertohtml(placeholder));
function placeholdertohtml(placeholder){
placeholder.html(placeholder.text().replace(/((\d{2},\d{2}))/g, function(match, number){
var blocks = placeholder.text().split(',').length;
console.log(blocks);
var el = number.substr(0,5).split(',');
var prefix = el[0];
var suffix = el[1];
var t = setInterval(function(){
if (blocks) {
var content = '<p><button>'+prefix+'</button><button>'+suffix+'</button></p>';
$('#placeholder').append(content);
blocks--;
} else {
clearInterval(t);
}
}, 100);
}));
}

So, I reworked your code using for loops instead of the replace function to fix the problem.
Here is a working codepen
I basically made a loop that built up an array of html to add:
var numberOfPairs = placeholder.text().match(/((\d{2},\d{2}))/g).length;
var countdown = numberOfPairs;
var string = placeholder.text();
var elements = [];
for(var i = 0; i < numberOfPairs; i++) {
var pair = string.substring(5 * i + 1, (5 * i) + 6).split(',');
var prefix = pair[0];
var suffix = pair[1];
elements.push('<p><button>'+prefix+'</button><button>'+suffix+'</button></p>');
}
And then looped over the elements with your interval function to get the same "loading" effect:
var elementIndex = countdown;
var t = setInterval(function(){
if (countdown >= 0) {
$('#placeholder').append(elements[(countdown - elementIndex) * -1]);
countdown--;
} else {
clearInterval(t);
}
}, 100);

Related

Loop reload same trial instead of next one Qualtrics

I have a problem with Qualtrics Loop and Merge + Javascript. Basically, Qualtrics reload the previous trial of my loop instead of a new one.
First, here is what I try to do: I have two sets of pictures, at each loop, one picture of each set is randomly picked and displayed (randomly to the left or right side). At the end of the loop, each pictures of the set will be displayed (33 in each set, so 33 trials of the loop), without picking twice the same picture. At each trial, I also display randomly the name of the condition "in relationship" or "friends"
The problem in details:
I have coded something that seems to work quite well, except at some point: It happens that when loading the next trial of the loop, it doesn't display new pictures but instead the one that were presented in the previous trial (and also the same name of condition). This can go for a while presenting me always the same pictures, or this can just be one time.
In any case, this trial which is represented count as a trial, as my loop always ends after 33 trials, whatever if some of these are actually not new pictures presented.
This problem doesn't appear always at the same trial, and can appear several times in a loop.
It also happened that only one picture displayed is new, and the other is the one presented in the previous trial. (I really don't get how this can happen).
I coded the randomisation of picture in an empty question, just before the question where it displays the pictures. This empty question is supposed to not require to press the "next" button. When the trial are correct, this is working well, but when the next trial is going to be one "reloaded" trial, it requires to press the "next" button.
I tried the survey with Firefox and Edge, and the problem is the same.
My javascript code:
In the first question of the survey:
Qualtrics.SurveyEngine.addOnload(function () {
function shuffle(o) { //v1.0
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
var FacesM_list = "IM_7U04c3TQlA7dT3D,IM_eE5iBcKfoOblbJH,IM_a3mklzA9E1OuRWl,IM_bQnoSJOGwa0vn9P,IM_5inNCVgPdHHTVSR,IM_bHlurQJWSXDRDFj,IM_3rCr2DIzW2cvqLP,IM_509x1wz7pM6PP0N,IM_3UGdsp02IcCqaSV,IM_eLrY7bwDPiT7apn,IM_3LdlHnBb6tnkBEh,IM_3pY6z6JDgaDvwq1,IM_9HtZxBS79DiOfR3,IM_03c9pDSpcwcqIyF,IM_6WGKJOzUWK4TJat,IM_2rxQPEGO7SEvsY5,IM_9YN3UCUtWEWTfGR,IM_8jZTSUAGJfuVECV,IM_9nQkFhRY2cLIVgN,IM_2abcJA7B79jt30h,IM_cD31N8XPTGliUN7,IM_0eL8iQd4PVdyuQl,IM_cuOoV9gSAe6CWd7,IM_9Nv3X7lWYEsTzsF,IM_5ccXAuuomEEyamp,IM_9mnvThFiNA5U84t,IM_e3UGNNuMdrKH8cl,IM_3aggsd5P9MMlUDr,IM_4ORY6GEMW8CmNPT,IM_50WOBkz8ADTFGHH,IM_3rqtlVBfijYCccZ,IM_3CzDsr0tYv7PH5H,IM_4SmeprjDgOeCl5b"
var FacesF_list = "IM_6fkHDs5f6ItAuk5,IM_0ri9MLjDhHxyonP,IM_bKlHtoAaxnBFlnT,IM_1WUqtBPpdhERpjf,IM_ac0yWos8tAqSMNT,IM_3xCePACn1Lq97tH,IM_6o1ZPLGUM682Au1,IM_babATdN3VtBLIsl,IM_8HSUICLvFvDXaN7,IM_0ebTztq3ML5Zh0V,IM_3lB8j5dhMs8i8ip,IM_0iC0pwDlpOkcTGt,IM_cIRojwU6sx3W7Od,IM_9ZHNbignrAfcThX,IM_8iFXvVcCqk5hemN,IM_6rrwImdl4Nss0u1,IM_6mPEaoIdazwqAWp,IM_b8lrxhsPGcc1HaR,IM_23uYWeF2gYVMsap,IM_6ycfrm5xOlewjFb,IM_7UKValFGc9Kmpp3,IM_8Bbkzsmc7CyMvqt,IM_d5S95FnSgo8j06F,IM_brXT4VUU8QJiRwN,IM_9MEkpgEmOwXhril,IM_6KG9qokOlD16GDH,IM_ellgVnYbtb8ZSbb,IM_eg6qSYMQ56z5JpX,IM_5vfbDNPdZeP1XCZ,IM_cDbOprwCUUSnUZT,IM_cumIGHXOFByV4Pz,IM_0jh2Va4JTfGsQDz,IM_0CGlFRy4dW8lDcF"
var FacesM_order = [];
for (var i = 1; i <= 33; i++) {
FacesM_order.push(i);
}
FacesM_order = shuffle(FacesM_order);
var FacesF_order = [];
for (var i = 1; i <= 33; i++) {
FacesF_order.push(i);
}
FacesF_order = shuffle(FacesF_order);
var nTrial = 0;
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_order", FacesM_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_order", FacesF_order.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesF_list", FacesF_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("FacesM_list", FacesM_list.toString());
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
});
And in the first question of the Block with Loop and Merge:
Qualtrics.SurveyEngine.addOnload(function () {
var nTrial = Number("${e://Field/nTrial}") + 1;
var FacesF_list = "${e://Field/FacesF_list}".split(',');
var FacesM_list = "${e://Field/FacesM_list}".split(',');
var FacesF_order = "${e://Field/FacesF_order}".split(',');
var FacesM_order = "${e://Field/FacesM_order}".split(',');
var FacesF = FacesF_list[FacesF_order[nTrial]];
var FacesM = FacesM_list[FacesM_order[nTrial]];
var rand = Math.random()
if (rand < 0.5) {
var left = FacesF
var right = FacesM
} else {
var left = FacesM
var right = FacesF
}
Qualtrics.SurveyEngine.setEmbeddedData("nTrial", nTrial.toString());
Qualtrics.SurveyEngine.setEmbeddedData("left", left.toString());
Qualtrics.SurveyEngine.setEmbeddedData("right", right.toString());
this.clickNextButton.delay(.00000000000001);
});
Thanks a lot for your help!

Show a specific number of random objects from a selection and hide all others with js/jQuery?

What I did so far was the following:
function randomSelectObjects(randObjects, countShow){
var i = 0;
var countRandObjects = randObjects.length;
var preselectedObj = false;
randObjects.hide(); // hide all items
while (i < countShow) { // while until we found enough items we can show
preselectedObj = randObjects.eq(Math.floor(Math.random()*countRandObjects)); // random select an object
if(preselectedObj.is(':hidden')){ // make sure it is not already unhidden
preselectedObj.show(); // show the object
i++; // up the counter – done only in case it was not already visible
}
}
}
Usage:
var randObjects = $('.items');
randomSelectObjects(randObjects, 1);
The problem is that I will run into selecting an already revealed (show()) item inside while from time to time. I would love to remove that unnecessary overhead.
Unfortunately there seems to be no way to remove an object from a cached selection. remove() also removes the object from the DOM which is not (always) what I want.
Cloning the selection of objects first and then using remove() would work for the selection process but then there would be the overhead to match the selected items with the live DOM for actually show() them.
My suggestion is create a unique random array first. Hide all elements then loop through the array of random inidices and show matching elements
// wrap in simple jQuery plugin
$.fn.randomDisplay = function(max_items) {
max_items = max_items || 5;
//create array of unique random indices
var randArr = randArray(this.length, max_items);
// hide all then filter matches to show
this.hide().filter(function(i){
return randArr.indexOf(i) >-1
}).show();
// creates unique array
function randArray(max, len) {
var arr = [], rand;
for (var i = 0; i < len; i++) {
rand = getRand(max)
while (arr.indexOf(rand) > -1) {
rand = getRand(max)
}
arr.push(rand);
}
return arr;
}
// random number helper
function getRand(max) {
return Math.floor(Math.random() * max)
}
}
// use
$(function(){
$('.item').randomDisplay(7)
})
DEMO

How to save the new order of a reordered list locally? (Javascript/Jquery--Not Sortable Plugin)

I have an in progress fiddle saved here (that you can see I've edited a painfully many number of times trying to figure this out): http://jsfiddle.net/JsJs2/182/
I have a basic set of html I've been working with to test this:
<div id="inventoryBlock">
<div class="list_bg">Hello</div>
<div class="list_bg">World</div>
<div class="list_bg">Whats</div>
<div class="list_bg">Going</div>
<div class="list_bg">Down?</div>
</div>
Undo
<div id="echo"></div>
The idea is to randomly sort this list on a user's first visit to the page only. Then anytime they return to this page within the set time, they will see the list sorted exactly as it was when they first visited the page (the same order as that first random sort). Here is the script I've been working on:
//var orig = $("#inventoryBlock").children(".list_bg");
$(document).ready(function () {
var now = (new Date()).getTime();
var lastTime = 0;
var lastTimeStr = localStorage["lastTime"];
if (lastTimeStr) lastTime = parseInt(lastTimeStr, 10);
if (now - lastTime > 1 * 60 * 1000) {
reorder();
} else {
$("#inventoryBlock").html(elements);
}
localStorage["lastTime"] = "" + now;
});
//function 'reorder' is declared
function reorder() {
//variable 'grp' is defined as the .list_bg elements
//variable 'grp' is an empty jquery object, not an array--an array has '[]' in the variable
var grp = $("#inventoryBlock").children();
//variable 'cnt' is defined as the number of .list_bg elements
var cnt = grp.length;
//variables 'temp' and 'x' are declared
var temp, x;
//variable 'i' is declared and defined as 0, a loop is initiated to continue until the value of 'i' is no longer less than the value of 'cnt'
// While there remain elements to shuffle...
for (var i = 0; i < cnt; i++) {
//the 'temp' variable is defined as the current .list_bg item, assigned an order, and set as an array value using the associated 'i' value
// Pick a remaining element...
//temporaryValue = array[currentIndex]
temp = grp[i];
//the 'x' value is assigned randomly based on the 'cnt' value (use javascript random function)
x = Math.floor(Math.random() * cnt);
// And swap it with the current element.
//array[currentIndex] = array[randomIndex];
grp[i] = grp[x];
//array[randomIndex] = temporaryValue;
grp[x] = temp;
}
//returns empty jquery object
//removes original object
$(grp).remove();
//appends new object/replacement object
$("#inventoryBlock").append($(grp));
console.log($(grp).get());
var elements = $('#inventoryBlock').find('.list_bg');
}
function orderPosts() {
$("#inventoryBlock").html(orig);
}
$("#undo").click(function (e) {
e.preventDefault();
orderPosts();
});
The bottom function orderPosts() is not really relevant here, and the echo div I was trying to use to echo values so I could better figure out what to do. The inventoryBlock is the only div that matters. The whole idea is to save the list order to data after that first page visit, and to load that data when they revisit the page within that set time period. I'm stuck on how to achieve this. I've tried too many suggestions to even recount, have tried commenting and recommenting the code so that I could figure it out step by step. I'm just missing something. Could someone point me in the right direction?
It looks like you are only putting the timestamp in localStorage. I think you need to put the shuffled order in localStorage too.
// Remember this for the "undo" button.
var origHtml = $("#inventoryBlock").html();
$(document).ready(function () {
var now = (new Date()).getTime();
var doReorder = true;
var lastTimeStr = localStorage["lastTime"];
if (lastTimeStr) {
var lastTime = parseInt(lastTimeStr, 10);
if (now - lastTime <= 1 * 60 * 1000) {
// It hasn't been a minute, so use the stored order.
$("#inventoryBlock").html(localStorage["html"]);
doReorder = false;
}
}
if (doReorder) {
reorder();
localStorage["lastTime"] = "" + now;
localStorage["html"] = $("#inventoryBlock").html();
}
});
$("#undo").click(function (e) {
e.preventDefault();
localStorage["lastTime"] = "";
localStorage["html"] = "";
$("#inventoryBlock").html(origHtml);
});
jsfiddle

How to proper use setTimeout with IE?

For a mockup-webpage used for research on interaction on websites, I created a mockup message-stream using JavaScript. This message stream should show images at pre-set intervals. In Chrome, this code posts the images below one another, at the pre-set intervals. In IE, only the first image is shown. I have already removed the passing of parameters in the window.setTimeout method, but am lost as to what else I need to do to satisfy IE. What else should I do to make all images appear in the pre-set intervals?
My script-section has several global variables:
var updateinterval = 400; // a multiplier (integer) used to
var scrollinterval = 5; // an interval (integer) to wait before scrolling
var point; // integer used to prevent using parameters in window.setTimeout
var interval = [0, 10, 40]; // array of integers creating diversity in intervals
var images = ["r1", "a1", "r2"];// array of strings referring to images to show
The following functions are present:
function trypost(){
point = point + 1;
if(point < interval.length){
//write the required image
document.writeln("<img src='images/"+images[point]+".png'/><br/>");
//time scroll to bottom
var stb = window.setTimeout(scrollToBottom, scrollinterval);
//time next post
var nextupdate = interval[point]*updateinterval;
var tp = window.setTimeout(trypost, nextupdate);
}
}
function scrollToBottom(){
window.scrollBy(0,document.body.scrollHeight);
}
function startpost(){
point = -1;
trypost();
}
window.onload = startpost;
You can use setInterval instead of repeatedly calling setTimeout, setInterval repeats until you call clearInterval.
Also as noted in the comments, document.writeln is from a different decade ;)
You can modify the DOM directly now.
var i=0;
var interval = [0, 10, 400, 10, 1000];
var images = [
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv",
"http://img2.wikia.nocookie.net/__cb20120327004334/mrmen/images/a/a0/MrMean.gif",
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv",
"http://img2.wikia.nocookie.net/__cb20120327004334/mrmen/images/a/a0/MrMean.gif",
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRTiW-7zqLiG1DNq4Tmt6x4j1iBc0FZRBpyYZtIXgDzUy_NHwTv"
];
for(var i=0; i<interval.length;i++)
var timer = setTimeout(function(img){
var newImg = document.createElement("IMG");
newImg.src = img;
document.getElementById('holder').appendChild(newImg);
}, interval[i], images[i]);
with this HTML
<div id='holder'>
</div>
Running demo
http://jsfiddle.net/W7QXH/4/

Javascript easing not animating

Trying to implement simple easing in javascript (without jQuery) but unfortunately there doesn't seem to be any animation when the button is clicked (see below).
My goal is to get the hidden list item (the last item) visible by tweening the first item's margin left property. I know it isn't a CSS issue because manually modifying the style moves the list, but I'm not sure what the issue is. My guess is with how I'm calling the ease function but changing the params still wasn't working for me.
The easing part is below, entire code is here: Fiddle
JS:
var start = document.getElementById('start'),
list = document.getElementById('my-list'),
imgs = list.getElementsByTagName('img'),
last_img = imgs[imgs.length -1 ];
ease = function(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
},
shift_imgs = function(el) {
var orig_value = parseFloat( el.style.marginLeft ),
end_value = -37,
change = Math.abs( end_value - orig_value ),
duration = 1, // 1 second
time = 0;
for ( var i = 0; i < change; i++ ) {
setTimeout(function() {
el.style.marginLeft = ( parseFloat( el.style.marginLeft ) + 1 ) + 'px';
}, time);
time = ease(time, orig_value, change, duration);
}
};
start.onclick = function() {
shift_imgs(last_img);
}
Your orig_value is NaN as parseFloat(el.style.marginLeft) returns nothing, even if you set an initial value in the css. i.e: margin-left: 15px; still will return nothing.
You can use window.getComputedStyle(...).getPropertyValue, similar to this:
window.getComputedStyle(el, null).getPropertyValue("margin-left");
This will give you the actual current value along with the px, i.e: 0px.
(It always return the value in px even if set in CSS as em or pt)
So you need to remove the px and the get the float value.
You can wrap this into a little helper similar to this:
getElementMarginLeftAsFloat = function (el) {
var pxValue = window.getComputedStyle(el, null).getPropertyValue("margin-left");
var valueOnly = pxValue.substring(0, pxValue.length - 2);
return parseFloat(valueOnly);
}
Another issue is that the moving of the actual element occurs within setTimeout executed inside a loop. The loop which calls setTimeout, causes each setTimeout to be queued nearly simultaneously, hence they all execute close to the same time, causing the element to just jump.
You can use a recursive sub-method inside your method which uses setTimeout to call itself until it is done. That way each setTimeout is triggered only after the specified interval, causing them to be executed close enough apart to the specified interval, similar to this:
shift_imgs = function (el) {
var orig_value = getElementMarginLeftAsFloat(el),
end_value = -37,
change = Math.abs(end_value - orig_value),
duration = 1, // 1 second
time = 0;
function doShift() {
currentValue = getElementMarginLeftAsFloat(el);
if(currentValue+1 > change){
return;
};
el.style.marginLeft = (currentValue + 1) + 'px';
time = ease(time, orig_value, change, duration);
setTimeout(doShift, time);
}
doShift();
};
By having the setTimeout function call itself, it releases the resources, ensuring the drawing of the element can occur between each "iteration".
I updated your code to use this approach and it seems to work now.
DEMO - animating movement using computed style
You can most likely do this many other ways and also prettify this code for sure but but this should get you started either way.

Categories