Weird jQuery behaviour. Mouse down not calling .text() - javascript

I have this code:
$( "#drawingSurface" ).mousedown(function(event) {
$('.infoText').text('Started! Please Wait..');
compilation(); // this takes a long time.
}
Now no matter what I do, $('.infoText').text('Started! Please Wait..'); will just not execute. The element is there, it is annoying because compilation takes ages to finish and when it is done then the text shows up. I want the text to SHOW up as in the order that I have written things.

It's executing, but while compilation is running, it prevents the browser from updating the display of the page.
You can fix that by introducing a very brief delay between the text update and compilation:
$( "#drawingSurface" ).mousedown(function(event) {
$('.infoText').text('Started! Please Wait..');
setTimeout(compilation, 50);
});
That way the browser has a chance to show the change before blocking on the long-running compilation. (0 usually works on Chrome, but Firefox typically needs about 50. YMMV.)
If you can, consider moving compilation to a web worker (spec | MDN) so you don't have to block the main UI thread at all.

Seems like the DOM update does not behave synchronously (edit: not exactly, see comments), so it begins executing your function (compilation()) before the text in the div changes. As a workaround, you could add it to a timeout such as the following
$( () => {
$( "#drawingSurface" ).mousedown(function(event) {
console.log('hi');
$('.infoText').text('Started! Please Wait..');
setTimeout(doWork, 1);
});
});
function doWork() {
let i = 0;
while (i < 1e3) {
console.log('waiting');
i++
}
$('.infoText').text('Done.');
console.log('finished');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="drawingSurface">
Click me
</div>
<div class="infoText">
text
</div>

Alternatively, you could move '$('.infoText').text('Started! Please Wait..');' to be inside the compilation function.

Related

setTimeout 0 and blocking function issues in Firefox

I came across a situation were a button event handler calls a function, that may take a couple seconds to complete depending on the input. Once the function completes, the output will show up in a grid.
The function is completely client side. Right before the function is running I add a css class to the grid wrapper div that basically just shows a 'loading' gif/animation.
This works fine in Chrome, but not in Firefox and IE 11.
Below is an oversimplified version of how I achieve this with javascript setTimeout 0.
$('#calc').on('click', function(){
$('#container').addClass('loading');
calculate(10, function(res){
$('#result').text(res);
$('#container').removeClass('loading');
});
});
//represents my long running function
function fib(n) {
if(n<2) {
return n;
}
return fib(n-2) + fib(n-1);
}
//will be called by click handler
function calculate(n,cb) {
setTimeout(function(){
var result = fib(n);
return cb(result);
},0)
}
As you can see I use setTimeout 0 in calculate(n,cb) to give the browser the ability to show the 'loading' animation before the function starts and then remove it when it is done.
However, this does not work in Firefox.
What are some other options for me to achieve what I am trying to do?
I am using jQuery here, but the actual project I am working on is using Angular5. But the idea should be the same.
Here is a jsFiddle to show what I am trying to do. Using the loading animation by Mattln4D (thanks)
https://jsfiddle.net/alabianc/qL5zggh7/
If you want to see some actual good result, run it with 40 as an input in calculate, but no more than that!
I appreciate any help!
I think if you set the timeout = 0, it is so fast to browser can show the loading animation. When I try to change timeout = 10 or 100, I can see the loading animation show on both of chrome, ff.

keyup function performing weird

So I got a little codepen. Everything works so far except a little thing. I got a <h1> and an <input>. When I type something in the text input, its value should get passed to the <h1> in realtime.
I tried to do that with a keyup function:
$('input[name=titleInput]').keyup(function(){
$('#title').text(this.value);
});
Something happens, but not what I want.When I type something in the text input, then delete it (with backspace) and re-enter something, only the first character gets passed to the title.Try it out on my codepen. Maybe it's just a stupid mistake of mine, but to me this behaviour is pretty weird.Thanks for your help in advance!EDIT:I am using text-fill-color, which may causes the problem.EDIT 2:A friend of mine tested it. It worked for her. She's using Chrome and the same version as me (58.0.3029.110 (official build) (64-Bit)).
Chrome does not update the content correctly. Such kind of bugs can always happen if you use vendor prefixed css properties, so you should avoid those.
You could hide the container before update, and then show it again with a timeout. This will trigger an update, but would also result in flickering.
$('input[name=titleInput]').keyup(function(){
$('.clipped').hide()
$('#title').text(this.value);
setTimeout(function() {
$('.clipped').show();
})
});
EDIT An alternative might be to use background-clip on the text and provide the inverted image yourself, but I right now don't have time to test that.
EDIT2 Based on the test of #TobiasGlaus the following code does solve the problem without flickering:
$('input[name=titleInput]').keyup(function(){
$('.clipped').hide().show(0)
$('#title').text(this.value);
});
This seems to be different to $('.clipped').hide().show() most likely it starts an animation with duration 0 and uses requestAnimationFrame which also triggers a redraw. To not relay on this jQuery behaviour, the code should be written as:
$('input[name=titleInput]').keyup(function(){
if( window.requestAnimationFrame ) {
$('.clipped').hide();
}
$('#title').text(this.value);
if( window.requestAnimationFrame ) {
window.requestAnimationFrame(function() {
$('.clipped').show();
})
}
});
i'd use the following lines:
$('input[name=titleInput]').bind('keypress paste', function() {
setTimeout(function() {
var value = $('input[name=titleInput]').val();
$('#title').text(value);
}, 0)
});
This will listen to the paste / keypress events, and will update the value on change.

SetInterval loop relatively confusing to me

HTML
<div id="backspace" ng-click="deleteString(''); decrementCursor();">
JS
<script>
$scope.deleteString = function() {
if($scope.cursorPosVal > 0){
//$scope.name = $scope.name - letter;
$scope.name = [$scope.name.slice(0, $scope.cursorPosVal - 1) + $scope.name.slice($scope.cursorPosVal)].join('');
console.log($scope.name);
setTimeout(function(){ setCaretPosition("inputBox", $scope.cursorPosVal); }, 30);
} else {
$scope.cursorPosVal = 1;
}
};
</script>
I am designing an on screen touchscreen keyboard. This is my backspace button. I am going to make it so that when you click and hold the backspace button, it starts removing characters automatically. I don't know where to begin with creating a setInterval, and I know a setInterval is exactly what I need to use here.
If I'm not wrong, you want that while you're keeping your button pressed, a function repeats itself.
You're right with setInterval(). However, the way you manage the event is wrong.
Take a look at this fiddle (It's not your code, but a simple example is the best way to understand):
http://jsfiddle.net/daq9atdd/1/
$(function(){
var interval = null;
$('#myButton').mousedown(function(){
interval = setInterval(function(){
console.log('Hello !');
}, 250);
});
$('#myButton').mouseup(function(){
clearInterval(interval);
});
});
I start the interval when the button is pressed, store it, and clear it when the button is released.
You’re so sure about setInterval.
If browser briefly hangs for whatever reason (say some background task), setInterval would go on queueing your backspace calls until it has some CPU time. This means user may see no change and hold backspace longer than needed, and then see a whole bunch of characters suddenly vanish when browser is back to normal.
Thus by setting a timeout after every call you’re making sure user won’t remove more characters than needed. Might be important if the goal is to improve UX.
Example implementation with AngularJS directives and setTimeout
See also:
setTimeout or setInterval?
noKid’s fiddle updated with setTimeout in mind

force DOM redraw with javascript on demand

The title of the question expresses what I think is the ultimate question behind my particular case.
My case:
Inside a click handler, I want to make an image visible (a 'loading' animation) right before a busy function starts. Then I want to make it invisible again after the function has completed.
Instead of what I expected I realize that the image never becomes visible. I guess that this is due to the browser waiting for the handler to end, before it can do any redrawing (I am sure there are good performance reasons for that).
The code (also in this fiddle: http://jsfiddle.net/JLmh4/2/)
html:
<img id="kitty" src="http://placekitten.com/50/50" style="display:none">
<div>click to see the cat </div>
js:
$(document).ready(function(){
$('#enlace').click(function(){
var kitty = $('#kitty');
kitty.css('display','block');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec)
{
var endtime= new Date().getTime() + usec;
while (new Date().getTime() < endtime)
;
}
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.css('display','none');
});
});
I have added the alert call right after the sleepStupidly function to show that in that moment of rest, the browser does redraw, but not before. I innocently expected it to redraw right after setting the 'display' to 'block';
For the record, I have also tried appending html tags, or swapping css classes, instead of the image showing and hiding in this code. Same result.
After all my research I think that what I would need is the ability to force the browser to redraw and stop every other thing until then.
Is it possible? Is it possible in a crossbrowser way? Some plugin I wasn't able to find maybe...?
I thought that maybe something like 'jquery css callback' (as in this question: In JQuery, Is it possible to get callback function after setting new css rule?) would do the trick ... but that doesn't exist.
I have also tried to separte the showing, function call and hiding in different handlers for the same event ... but nothing. Also adding a setTimeout to delay the execution of the function (as recommended here: Force DOM refresh in JavaScript).
Thanks and I hope it also helps others.
javier
EDIT (after setting my preferred answer):
Just to further explain why I selected the window.setTimeout strategy.
In my real use case I have realized that in order to give the browser time enough to redraw the page, I had to give it about 1000 milliseconds (much more than the 50 for the fiddle example). This I believe is due to a deeper DOM tree (in fact, unnecessarily deep).
The setTimeout let approach lets you do that.
Use JQuery show and hide callbacks (or other way to display something like fadeIn/fadeOut).
http://jsfiddle.net/JLmh4/3/
$(document).ready(function () {
$('#enlace').click(function () {
var kitty = $('#kitty');
// see: http://unixpapa.com/js/sleep.html
function sleepStupidly(usec) {
var endtime = new Date().getTime() + usec;
while (new Date().getTime() < endtime);
}
kitty.show(function () {
// simulates bussy proccess, calling some function...
sleepStupidly(4000);
// when this triggers the img style do refresh!
// but not before
alert('now you do see it');
kitty.hide();
});
});
});
Use window.setTimeout() with some short unnoticeable delay to run slow function:
$(document).ready(function() {
$('#enlace').click(function() {
showImage();
window.setTimeout(function() {
sleepStupidly(4000);
alert('now you do see it');
hideImage();
}, 50);
});
});
Live demo
To force redraw, you can use offsetHeight or getComputedStyle().
var foo = window.getComputedStyle(el, null);
or
var bar = el.offsetHeight;
"el" being a DOM element
I do not know if this works in your case (as I have not tested it), but when manipulating CSS with JavaScript/jQuery it is sometimes necessary to force redrawing of a specific element to make changes take effect.
This is done by simply requesting a CSS property.
In your case, I would try putting a kitty.position().left; before the function call prior to messing with setTimeout.
What worked for me is setting the following:
$(element).css('display','none');
After that you can do whatever you want, and eventually you want to do:
$(element).css('display','block');

Changing Javascript Focus in onClick event?

Note: A possible solution needs only work in Firefox 3.0, my app doesn't allow access from IE! =)
I have a link that, when clicked, will display a lightbox to the user:
show lightbox
My problem is that when the lightbox is displayed, the focus remains on the link. So if the user presses the up or down keys, they end up scrolling the main document, not the lightbox that is displayed!
I've tried to set the focus to the lightbox element using code like this
function focus_on_lightbox() {
document.getElementById('lightbox_content').focus();
}
This works fine if I type it in the firebug console, but will not work if I include it at the end of the onclick snippet. It appears as though I can't change the focus away from the link from code executed inside the onclick event?
-- Update 1
I've tried something like this before
show lightbox
and I've modified function to add some debugging output, as follows
function focus_on_lightbox() {
console.log('hihi');
console.log(document.activeElement);
document.getElementById('lightbox_content').focus();
console.log(document.activeElement);
}
The output is as follows
hihi
<a onclick="closePopup();lightbox('apartment_detail','11619');focus_on_lightbox();return false;" href="#">
<a onclick="closePopup();lightbox('apartment_detail','11619');focus_on_lightbox();return false;" href="#">
So the focus before I did anything was on the link and after I tried to change the focus it still remained on the link?
Not sure if it matters, but I use ajax to load the lightbox, as follows
new Ajax.Updater(lightbox_content_id, url, {asynchronous:true, evalScripts:true, onLoading:show_lightbox_loading(), onComplete:focus_on_lightbox() });
I tried to set the focus after the ajax complete, but also, no luck.
What am I missing?
I've been trying to make a solution work that was suggested below by trying to set the focus, seeing if I succeeded by checking document.activeElement, if not, wait 100 milliseconds and try again. If I use the following function, it will work
function focus_on_lightbox() {
var seconds_waited = 0
pause(100);
var current_focus = document.activeElement
while (document.getElementById(lightbox_content_id) != current_focus && seconds_waited < 2000)
{
document.getElementById(lightbox_content_id).focus();
console.log(document.activeElement);
pause(100);
current_focus = document.activeElement
seconds_waited += 100;
}
}
However, if I remove the firebug debugging statment console.log, the function stops working!! I have no idea why this would be the case?? Why would outputting a variable to the firebug console affect weather focus is moved to the element or not? Does the console.log statement affect focus? perhaps by bringing the focus to the console debugging window?
I think your problem is calling your focus method after return false. your code should be like that :
<a href="#"
onclick="show_lightbox();focus_on_lightbox();return false;">
show lightbox
</a>
Here is the function that finally worked
function focus_on_lightbox(seconds) {
var seconds_waited
seconds_waited = seconds
document.getElementById(lightbox_content_id).focus();
seconds_waited += 100;
if (document.getElementById(lightbox_content_id) != document.activeElement && seconds_waited < 2000)
setTimeout("focus_on_lightbox(" + seconds_waited + ");", 100);
{
}
}
So why did console.log seem to affect setting the focus? Before I was using this function to pause between attempts of changing the focus.
function pause(milliseconds) {
var dt = new Date();
while ((new Date()) - dt <= milliseconds) { /* Do nothing */ }
}
This causes javascript to constantly be doing something and I think it wasn't giving the document time to render or update or something. The console.log seemed to break this lock and give the page a chance to change its focus.
When I changed approaches to using the timeout to pause between attempts, console.log was no longer needed!
Thanks bmoeskau for pointing me in the right direction.
In my experience, focus issues can sometimes be timing-related (e.g., focus() executes before the element is fully ready to be focused). I'm assuming that the lightbox markup is created dynamically when the show_lightbox function is called? If that's the case you could try adding a slight delay before attempting to focus to see if that's the issue, something like:
setTimeout("focus_on_lightbox();", 10);
Make the element focus itself. On the element's load event, set a timeout of a few ms and then call this.focus();
Else try jQuery.

Categories