setTimeout vs requestAnimationFrame - settimeout

I made an example of 'setTimeout vs requestAnimationFrame' to find out how different they are.
As you can see, the orange box arrives to the destination first. The green box jump some times and slower.
I understand why the green box jump some times. Because the task(calling move function) would not be inserted in macroTaskQueue before repaint some times(this is called jank or frame skip).
This is why I prefer requestAnimationFrame than setTimeout when animate element. Because the move() of requestAnimationFrame(move) is guaranteed to be called right before repaint.
Now, what I'm wondering is that why the green box is slower than orange box
Maybe does it mean that the move() is not called at each 1000/60 ms?

setTimeout is always late.
The way it works is
Register a timestamp when to execute our task.
At each Event loop's iteration, check if now is after that timestamp.
Execute the task.
By this very design, setTimeout() is forced to take at least the amount of time defined by delay. It can (and will often) be more, for instance if the event loop is busy doing something else (like handling user gestures, calling the Garbage Collector, etc.).
Now since you are requesting a new timeout only from when the previous callback got called, your setTimeout() loop suffers from time-drift. Every iteration it will accumulate this drift and will never be able to recover from it, getting away from the wall-clock time.
requestAnimationFrame (rAF) on the other hand doesn't suffer from such a drift. Indeed, the monitor's V-Sync signal is what tells when the event loop must enter the "update the rendering" steps. This signal is not bound to the CPU activity and will work as a stable clock. If at one frame rAF callbacks were late by a few ms, the next frame will just have less time in between, but the flag will be set at regular intervals with no drift.
You can verify this by scheduling all your timers ahead of time, your setTimeout box won't suffer from this drift anymore:
const startBtn = document.querySelector('#a');
const jankBtn = document.querySelector('#b');
const settimeoutBox = document.querySelector('.settimeout-box');
const requestAnimationFrameBox = document.querySelector('.request-animation-frame-box');
settimeoutBox._left = requestAnimationFrameBox._left = 0;
let i = 0;
startBtn.addEventListener('click', () => {
startBtn.classList.add('loading');
startBtn.classList.add('disabled');
scheduleAllTimeouts(settimeoutBox);
moveWithRequestAnimationFrame(requestAnimationFrameBox);
});
function reset() {
setTimeout(() => {
startBtn.classList.remove('loading');
startBtn.classList.remove('disabled');
i = 0;
settimeoutBox.style.left = '0px';
requestAnimationFrameBox.style.left = '0px';
settimeoutBox._left = requestAnimationFrameBox._left = 0;
}, 300);
}
function move(el) {
el._left += 2;
el.style.left = el._left + 'px';
if (el._left > 1000) {
return false;
}
return true;
}
function scheduleAllTimeouts(el) {
for (let i = 0; i < 500; i++) {
setTimeout(() => move(el), i * 1000 / 60);
}
}
function moveWithRequestAnimationFrame(el) {
if (move(el)) {
requestAnimationFrame(() => {
moveWithRequestAnimationFrame(el);
});
} else reset();
}
.grid {
margin: 30px !important;
padding: 30px;
}
.box {
width: 200px;
height: 200px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
color: white;
font-size: 18px;
}
.settimeout-box {
background-color: green;
}
.request-animation-frame-box {
background-color: orange;
}
<div class="ui grid container">
<div class="row">
<button class="ui button huge blue" id="a">Start!</button>
</div>
<div class="row">
<div class="box settimeout-box">
<span>setTimeout</span>
</div>
</div>
<div class="row">
<div class="box request-animation-frame-box">
<span>requestAnimationFrame</span>
</div>
</div>
</div>
Note that Firefox and Chrome actually do trigger the painting frame right after the first call to rAF in a non-animated document, so rAF may be one frame earlier than setTimeout in this demo.
requestAnimationFrame's frequency is relative to the monitor's refresh-rate.
Above example assumes that you run it on a 60Hz monitor. Monitors with higher or lower refresh rate will enter this "update the rendering" step at different frequencies.
Also beware, delay in setTimeout(fn, delay) is a long, this means the value you pass will be floored to integer.
An a last note, Chrome does self correct this time drift in its setInteval() implementation, Firefox and the specs still don't, but it's under (not so active) discussion.

Related

How to get work Infinite scroll depending on div container's scroll position?

I'm looking for a infinite scroll script, but can't find one which really fits my needs. I have found one which would be great, but the problem is that the script works after the User scrolls down using the browser SCROLLBAR. Instead it should load and display more data depending on the div "scroll" position.
How can i do that? It doesn't have to be this script but it should include a mysql query function so I can implement mine easily, because I'm really a newbie in Javascript, jQuery etc. A tutorial would do it also...but can't really find one. All of them are depending on the browser's scroll position.
That's the script I'm using:
github.com/moemoe89/infinite-scroll/blob/master/index.php
I made only one modification:
news_area{ width:400px; height:95px; overflow:auto;}
Thanks in advance!
Instead of $(window).scrollTop() use any container that you would want to get it's scrolling position from, like $('.scrollable').scrollTop().
$('.scrollable').on('scroll', function(){
var $el = $(this);
$('.scrolled').text($(this).scrollTop());
if( $el.innerHeight()+$el.scrollTop() >= this.scrollHeight-5 ){
var d = new Date();
$el.append('more text added on '+d.getHours()+':'+d.getMinutes()+':'+d.getSeconds()+'<br>');
}
});
div {
height: 50px;
border: 1px solid gray;
overflow: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Scrolled from top: <span class="scrolled">0</span></p>
<div class="scrollable">
text<br>
text<br>
text<br>
text<br>
text<br>
text<br>
</div>

How to: Fixed Table Header with ONE table (no jQuery)

I know, there are at least 3 dozen questions like this on stackoverflow and still, I could not make this happen:
A simple table where thead is sticked/fixed at the top, and the tbody is scrolled.
I tried so much in the past days and now I ended up here crying for help.
A solution should work in IE8+ and newest FF, Chrome & Safari.
The difference to other "possible duplicates like this one is that I don't want to use two nested tables or jQuery (plain javascript is fine though).
Demo of what I want:
http://www.imaputz.com/cssStuff/bigFourVersion.html.
Problem is it doesn't work in IE, and I would be fine to use some JS.
Ok i got it:
You need to wrap the table in two DIVs:
<div class="outerDIV">
<div class="innerDIV">
<table></table>
</div>
</div>
The CSS for the DIVs is this:
.outerDIV {
position: relative;
padding-top: 20px; //height of your thead
}
.innerDIV {
overflow-y: auto;
height: 200px; //the actual scrolling container
}
The reason is, that you basically make the inner DIV scrollable, and pull the THEAD out of it by sticking it to the outer DIV.
Now stick the thead to the outerDIV by giving it
table thead {
display: block;
position: absolute;
top: 0;
left: 0;
}
The tbody needs to have display: block as well.
Now you'll notice that the scrolling works, but the widths are completely messep up. That's were Javascript comes in.
You can choose on your own how you want to assign it. I for myself gave the TH's in the table fixed widths and built a simple script which takes the width and assigns them to the first TD-row in the tbody.
Something like this should work:
function scrollingTableSetThWidth(tableId)
{
var table = document.getElementById(tableId);
ths = table.getElementsByTagName('th');
tds = table.getElementsByTagName('td');
if(ths.length > 0) {
for(i=0; i < ths.length; i++) {
tds[i].style.width = getCurrentComputedStyle(ths[i], 'width');
}
}
}
function getCurrentComputedStyle(element, attribute)
{
var attributeValue;
if (window.getComputedStyle)
{ // class A browsers
var styledeclaration = document.defaultView.getComputedStyle(element, null);
attributeValue = styledeclaration.getPropertyValue(attribute);
} else if (element.currentStyle) { // IE
attributeValue = element.currentStyle[vclToCamelCases(attribute)];
}
return attributeValue;
}
With jQuery of course this would be a lot easier but for now i was not allowed to use a third party library for this project.
Maybe we should change a method to archieve this goal.Such as:
<div><ul><li>1</li><li>2</li></ul></div> //make it fixed
<table>
<thead>
<tr><th>1</th><th>2</th></tr>
</thead>
<tfoot></tfoot>
<tbody></tbody>
</table>
Of course, this is not good to sematic.But it is the simplest way without js or jq.
Don't you think so?

IE9 multiple select overflow while printing

I'm having problems with IE9 ignoring the select borders when printing a multiple select.
Here's how to recreate the problem:
Open IE9 on Windows 7.
Go to w3schools's multiple select edit page.
Now highlight the options and copy/paste until there is a long list of duplicates.
Then remove the size attribute.
Click on "Edit and Click Me" so that the page reloads and you now have your modified select in the second panel.
Now, print the document (even using the XPS viewer).
For me, all of the options are printed on the page, even though the select is only 4 option elements tall. This still happens to some degree if you leave the "size" attribute at the default value of 2, but it's far more obvious when it is changed or removed.
Is anyone else experiencing this? Is this an IE bug? Does anyone know of a workaround?
You can work around this by viewing the site in IE9's compatibility mode. Usually IE will determine that it cannot display a site properly and give you the option to turn on compatibility mode from within the address bar but sometimes you need to explicitly set it.
How to turn on compatibility mode - http://www.sevenforums.com/tutorials/1196-internet-explorer-compatibility-view-turn-off.html - I used the first one in method 2.
There doesn't seem to be any CSS solution for this. Instead, I wrote a small jQuery script that copies the <select multiple> contents into a <div>, so that it can be printed. Then I applied some CSS to make it look like a select, and only show the copy when actually printing.
Script:
//jQuery required
$(function() {
if(!$.browser.msie) return false;
$('select[multiple]').each(function() {
$lPrintableDiv = $('<div data-for="' + this.id + '" />').addClass($(this).attr('class')).addClass('printable');
//update printable on changes
$(this).after($lPrintableDiv).change(function($aEvent){
updatePrintable($aEvent.target);
});
//run once on load
updatePrintable(this);
});
});
function updatePrintable($aTarget) {
var $lSelect = $($aTarget);
var $lSelected = $($aTarget).val();
var $lPrintable = $('[data-for="'+$aTarget.id+'"]');
$($lPrintable).width($lSelect.width()).height($lSelect.height());
$($lPrintable).html('');
$($aTarget).children().each(function($lElm){
$lVal = $(this).val();
$lLabel = $('<label />').text($lVal);
$lOption = $('<input type="checkbox" />').val($lVal);
if($(this).is(':selected'))
$lOption.prop('checked', true);
$lPrintable.append($lOption).append($lLabel);
});
}
CSS:
.printable {
border: 1px solid grey;
display: none;
overflow: auto;
}
.printable label {
display: block;
font: .8em sans-serif;
overflow: hidden;
white-space: nowrap;
}
.printable [type="checkbox"] {
display: none;
}
.printable [type="checkbox"]:checked + label {
background: #3399ff;
color: white;
}
#media print {
select[multiple] { display: none; }
.printable { display: inline-block; }
.printable [type="checkbox"]:checked + label { background: grey; }
}
Also see the jsFiddle and original post about this fix

jquery ui connected sortables: cancel, disable, destroy... nada

I have two connected sortable lists.
For the sake of my question, when I start dragging an item from #sortable1 to #sortable2, in the start event I want to cancel/ disable/ the drop in #sortable2
Nothing works?
$("#sortable1, #sortable2").sortable({
connectWith: "#sortable1, #sortable2",
start: startDrag
});
function startDrag(event, ui) {
$("#sortable2").css("opacity","0.5");
// $("#sortable2").sortable("cancel"); // goes whooooo
/* docs say:
If the sortable item is being moved from one connected sortable to another:
$(ui.sender).sortable('cancel');
will cancel the change. Useful in the 'receive' callback.
*/
// $("#sortable1").sortable("cancel"); // I only want to cancel dropping in sortable2...
// $("#sortable2").sortable("disable"); // disables only after the drop event
// $("#sortable2").sortable("destroy"); // same as "disable"
}
function stopDrag(event, ui) {
$("#sortable2").css("opacity","1.0");
// $("#sortable2").sortable("enable");
}
My JSFiddle here: http://jsfiddle.net/tunafish/m32XW/
I have found 2 more questions like mine:
jQuery sortable('disable') from start event not entirely working like expected
http://forum.jquery.com/topic/disable-a-sortable-while-dragging-with-connectedlists
No responses..
Anything appreciated!
EDIT: I also tried to overlay a div like a modal on the sortable, but can still be dragged to that way. The only thing that worked is a show/hide, but that's not an option for me.
OK here is my app; two lists of images, sortable and you can copy over from the connected list.
If an item already exists in the target it's disabled.
Hopefully useful to someone...
JSFiffle here: http://jsfiddle.net/tunafish/VBG5V/
CSS:
.page { width: 410px; padding: 20px; margin: 0 auto; background: darkgray; }
.album { list-style: none; overflow: hidden;
width: 410px; margin: 0; padding: 0; padding-top: 5px;
background: gray;
}
.listing { margin-bottom: 10px; }
.album li { float: left; outline: none;
width: 120px; height: 80px; margin: 0 0 5px 5px; padding: 5px;
background: #222222;
}
li.placeholder { background: lightgray; }
JS:
$("ul, li").disableSelection();
$(".album, .favorites").sortable({
connectWith: ".album, .favorites",
placeholder: "placeholder",
forcePlaceholderSize: true,
revert: 300,
helper: "clone",
stop: uiStop,
receive: uiReceive,
over: uiOver
});
$(".album li").mousedown(mStart);
var iSender, iTarget, iIndex, iId, iSrc, iCopy;
var overCount = 0;
/* everything starts here */
function mStart() {
// remove any remaining .copy classes
$(iSender + " li").removeClass("copy");
// set vars
if ($(this).parent().hasClass("listing")) { iSender = ".listing"; iTarget = ".favorites"; }
else { iSender = ".favorites"; iTarget = ".listing"; }
iIndex = $(this).index();
iId = $(this).attr("id");
iSrc = $(this).find("img").attr("src");
iCopy = $(iTarget + " li img[src*='" + iSrc + "']").length > 0; // boolean, true if there is allready a copy in the target list
// disable target if item is allready in there
if (iCopy) { $(iTarget).css("opacity","0.5").sortable("disable"); }
}
/* when sorting has stopped */
function uiStop(event, ui) {
// enable target
$(iTarget).css("opacity","1.0").sortable("enable");
// reset item vars
iSender = iTarget = iIndex = iId = iSrc = iCopy = undefined;
overCount = 0;
// reinit mousedown, live() did not work to disable
$(".album li").mousedown(mStart);
}
/* rolling over the receiver - over, out, over etc. */
function uiOver(event, ui) {
// only if item in not allready in the target
if (!iCopy) {
// counter for over/out (numbers even/uneven)
overCount++;
// if even [over], clone to original index in sender, show and fadein (sortables hides it)
if (overCount%2) {
if (iIndex == 0) { ui.item.clone().addClass("copy").attr("id", iId).prependTo(iSender).fadeIn("slow"); }
else { ui.item.clone().addClass("copy").attr("id", iId).insertAfter(iSender + " li:eq(" + iIndex + ")").fadeIn("slow"); }
}
// else uneven [out], remove copies
else { $(iSender + " li.copy").remove(); }
}
// else whoooooo
}
/* list transfers, fix ID's here */
function uiReceive(event, ui) {
(iTarget == ".favorites") ? liPrefix = "fli-" : liPrefix = "lli-";
// set ID with index for each matched element
$(iTarget + " li").each(function(index) {
$(this).attr("id", liPrefix + (index + 1)); // id's start from 1
});
}
HTML:
<div class="page">
<div class="container">
<h2>Photo Album</h2>
<ul class="listing album">
<li id="li-1"><img src="tn/001.jpg" /></li>
<li id="li-2"><img src="tn/002.jpg" /></li>
<li id="li-3"><img src="tn/003.jpg" /></li>
<li id="li-4"><img src="tn/004.jpg" /></li>
<li id="li-5"><img src="tn/005.jpg" /></li>
</ul>
</div>
<div style="clear:both;"></div>
<div class="container">
<h2>Favorites</h2>
<ul class="favorites album">
<li id="fli-1"><img src="tn/001.jpg" /></li>
<li id="fli-2"><img src="tn/002.jpg" /></li>
<li id="fli-3"><img src="tn/010.jpg" /></li>
</ul>
</div>
</div>
/* docs say:
If the sortable item is being moved from one connected sortable to another:
$(ui.sender).sortable('cancel');
will cancel the change. Useful in the 'receive' callback.
*/
This code was what I spent 30 minutes looking for!
Ok I found some sort of hack for this.
When an item is moved from #sortable1 to #sortable2, when dragged over #sortable2 there is a list item added with class .placeholder
So in UI's over event I did
$("#sortable2 li.placeholder").hide();
Than set it back with UI's stop event
$("#sortable2 li.placeholder").show();
This is only for visuals though.. The item is still moved into #sortable2 so you need to remove() it there. To mimic copying you need to add a clone() back in #sortable2. You can get it's original index() in UI's start event and than use insertAfter(id - 1)
At the moment I can only clone in UI's receive event, I would prefer in UI's over but can't get it to work..

iPhone Unlock Slider With Mootools?

I try to make a slider similar to the iPhone unlock Slider, that forwards to a linked site, when complete, and returns to its initial position when the slider wasn't dragged to the end.
This is not meant to be a iPhone webapp, i just want to put this to a general website as a effect.
So far I've tried those two tests, but i'm stuck on both.
The first is:
// First Example
var el = $('slideOne'),
// Create the new slider instance
var sliderOne = new Slider(el, el.getElement('.slider'), {
steps: 20, // There are 35 steps
range: [8], // Minimum value is 8
}).set(20);
Problem here is that i can't fire an event on (0) not on (20) aswell, i tried onComplete but this fires the function immediatly after the page is load!?
The second
$$('.textslider').setStyle('opacity', 0.8);
$('slider1').makeDraggable({
snap: 0,
container: 'slideOne',
droppables: '.slidedrop1',
onDrop: function(element, droppable, event){
if (!droppable);
else console.log(element, 'dropped on', droppable, location = 'index.php/kredite.html', event);
},
});
Problem here is , that the droppable don't work as fine as i hoped, sometimes i move the the slider on the invisible droppable, which indicates if the slider is dragged to the end, and nothing happens, sometimes it works fine, i think this may be due the different position of the cursor on the slider. and i can't get it done that it is only possible to slide horizontal , since it is that drag not the slider function, so i think this won be the proper way.
On both Tests, i didn't figured out who i could return the slider back to its initial position, with a slide Effect.
Are there some mootools cracks around who maybe could help me with this? Thanks already for the great ideas of y'all.
<html>
<head>
<script type="text/javascript"
src="http://demos.mootools.net/demos/mootools.js"></script>
<script type="text/javascript">
window.addEvent('domready', function(){
var el = $('slideOne');
var debug = $('debug');
var ACTIVATE_VALUE = 20;
var mySlider = new Slider(el, el.getElement('.knob'), {
range: [0], // Minimum value
steps: ACTIVATE_VALUE, // Number of steps
onChange: function(value){
if (value == ACTIVATE_VALUE) {
debug.setStyles({color:'green'});
// go to url after delay
(function(){
location.href='http://joecrawford.com/';
}).delay(500);
} else {
debug.setStyles({color:'red'});
// if not activated, reset after 2 second delay
(function(){
value = 0;
mySlider.set(value);
}).delay(3000);
}
debug.set('text', value);
}
});
mySlider.set(0);
});
</script>
<style type="text/css">
div.slider {
width: 200px;
height: 50px;
background: #eee;
}
div.slider div.knob {
background: #000;
width: 50px;
height: 50px;
}
div#debug {
font-family: sans-serif;
font-size: xx-large;
}
</style>
<title>Slider that resets if it does not reach the end</title>
</head>
<body>
<div id="slideOne" class="slider">
<div class="knob"></div>
</div>
<div id="debug">-</div>
</body>
</html>