can you give me an example for chrome where i have something like this:
function checkVideoStatus(vidElement){
var currentTime = vidElement.currentTime;
//now wait 5 seconds
var updatedTime = vidElement.currentTime;
//now check if video has freezed
if(updatedTime == currentTime){
//fire videoFreeze Event
}
}
So i would want to fire this event here. Also, is it really necessary to fire this event, since i alredy can put a relevant method call here to do what i want accordingly??
Is it in JS the events are only like onChange, onMouseOver etc??
This is a simple mockup of a custom event and a countdown.
<div id="vid" data-current="0">Video</div>
var vid = document.getElementById('vid'),
freeze;
vid.addEventListener('freeze', function() {
console.log('Firing vid.freeze.... ' + this.dataset.current);
console.log('vid.freeze fired.');
});
(function go() {
var status;
vid.innerHTML = 'Video time: ' + vid.dataset.current;
if (vid.dataset.current < 5) {
status = vid.dataset.current == 0 ? 'Starting... ' : 'Continuing... ';
console.log(status + vid.dataset.current);
setTimeout(go, 1000);
} else {
freeze = document.createEvent('CustomEvent');
freeze.initEvent('freeze', true, true);
vid.dispatchEvent(freeze);
}
vid.dataset.current++;
})();
http://jsfiddle.net/userdude/Ampbm/1
This is mostly to show how to make a custom event which fires after five seconds. I'm also using the data- attribute to store the current time. What you're doing in the if block I'm not quite sure about, since .updatedTime and .currentTime seems to me to be possibly the same thing each go round.
Related
Forgive my naivety, this probably is quite obvious, I just can't see it now.
Please tell me what is wrong with the following code:
$('#iframe1').load(function(){
$('#iframe2').load(function(){
alert('loaded!');
});
});
The idea is to wait until both iframes have fully loaded, then alert "loaded" - of course this is a simplified example for the sake of stack.
The script sits in script tags at the end of the body of the html doc.
#Quertiy answer is perfectly fine, but not very jQuery-ish. It is hard-coded for 2 iframes only.
The beauty of jQuery is that you can make it work for the most number of people, with as little friction as possible.
I've advised a very simplistic plugin that does nearly what is present on that answer, but in a more open way. It not only works on iframes, but also on images, audio, video and whatever has a onload event!
Without further due, here's the code:
(function($){
$.fn.extend({allLoaded: function(fn){
if(!(fn instanceof Function))
{
throw new TypeError('fn must be a function');
}
var $elems = this;
var waiting = this.length;
var handler = function(){
--waiting;
if(!waiting)
{
setTimeout(fn.bind(window), 4);
}
};
return $elems.one('load.allLoaded', handler);
}});
})(window.jQuery);
It works by adding a load handler to every element in that selection. Since it is a plugin, you can use in whatever way you decide to use it.
Here's an example, that loads 30 random images:
//plugin code
(function($){
$.fn.extend({allLoaded: function(fn){
if(!(fn instanceof Function))
{
throw new TypeError('fn must be a function');
}
var $elems = this;
var waiting = this.length;
var handler = function(){
--waiting;
if(!waiting)
{
setTimeout(fn.bind(window), 4);
}
};
return $elems.one('load.allLoaded', handler);
}});
})(window.jQuery);
$(function(){
//generates the code for the 30 images
for(var i = 0, html = ''; i < 30; i++)
html += '<img data-src="http://lorempixel.com/g/400/200/?_=' + Math.random() + '">';
//stuffs the code into the body
$('#imgs').html(html);
//we select all images now
$('img')
.allLoaded(function(){
//runs when done
alert('loaded all')
})
.each(function(){
//the image URL is on a `data` attribute, to delay the loading
this.src = this.getAttribute('data-src')
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
<div id="imgs"></div>
Your problem, as said before many times, is that you have a load event attached to your iframe. That event is fired everytime the content change.
After that, you set a new event on #iframe2. When it's content changes, it will fire events left and right, above and beyound what you wish!
The best aproach is to keep track of which ones you loaded or not. After all have been loaded, you simply run the function.
The problem is that you're waiting until #iframe1 loads before you attach a handler for #iframe2 loading. So if #iframe2 loads first, you'll never get your callback.
Instead, watch the load event on both of them and track which ones you've seen:
var seen1 = false,
seen2 = false;
$('#iframe1, #iframe2').load(function(){
if (this.id == "iframe1") {
seen1 = true;
} else {
seen2 = true;
}
if (seen1 && seen2) {
alert('loaded!');
}
});
Why do you expect 2nd iframe to load after the first one?
~function () {
var loaded = 0;
$('#iframe1, #iframe2').load(function (){
if (++loaded === 2) {
alert('loaded!');
}
});
}()
I've been messing around with YouTube API to make a button that copies the video ID on click using jQuery. I have no idea why, I don't get any Javascript errors and after hours (actually been working on this problem for 2 days), I still haven't figured out why does the eventListener doesn't fire.
The eventListener is bound at the end of the function, which is fired when you hit a search button. Each time you click on the search button or on the next page/ previous page buttons, they fire the makeRequest function. Somehow, though, the function triggered by the eventListener never fires (the function code isn't in this parcel of code itself, but it's actually just console.log's). Can anyone help? Would be really appreciated <3
function makeRequest(e) {
$('#search-container')[0].innerHTML = '';
ytSearchResults = [];
var q = $('#query').val();
var request = gapi.client.youtube.search.list({
q: q,
part: 'snippet',
maxResults: 15,
pageToken: ytPage,
type: 'video',
safeSearch: 'none',
videoSyndicated: 'true'
});
request.execute(function(response) {
var str = response.result;
ytNextPage = str.nextPageToken;
ytPrevPage = str.prevPageToken;
for(i=0;i<str.items.length;i++){
$('#search-container')[0].innerHTML += '<br><image src='+str.items[i].snippet.thumbnails.default.url+'></image><br><button id="'+str.items[i].id.videoId+'" class="NCScopiable">'+str.items[i].snippet.title+'</button>';
ytSearchResults.push(str.items[i].id.videoId);
}
$('#search-container')[0].innerHTML += '<br><div id="button-changePage"><span id="button-prevPage" style="color:blue;cursor:pointer;margin:5px"><u>Previous Page</u></span><span id="button-nextPage" style="color:blue;cursor:pointer;margin:5px"><u>Next Page</u></span>';
if(e&&ytCurPage>0){
$('#button-prevPage')[0].style.display = 'block';
$('#button-nextPage')[0].style.display = 'block';
} else {
ytCurPage = 0;
$('#button-prevPage')[0].style.display = 'none';
}
$('#button-prevPage').on('click',function(){ytCurPage-=1;ytPage=ytPrevPage;makeRequest(true);});
$('#button-nextPage').on('click',function(){ytCurPage+=1;ytPage=ytNextPage;makeRequest(true);});
console.log('Current ytCurPage value: '+ytCurPage);
});
$('.NCScopiable').on('click',cTWI);
}
The problem is that the line
$('.NCScopiable').on('click',cTWI);
Is being executed before the code inside the callback passed to request.execute has fired, so those elements don't exist yet. Thus $('.NCScopiable') is returning nothing.
Move that line into the last line of your callback code and you should be good to go, for example:
request.execute(function(response) {
var str = response.result;
ytNextPage = str.nextPageToken;
ytPrevPage = str.prevPageToken;
// ...code removed for brevity...
// this was the code that is actually creating the elements with
// the NCScopiable class
console.log('Current ytCurPage value: '+ytCurPage);
$('.NCScopiable').on('click',cTWI);
});
I'm having a bit of a jquery javascript performance issue, specifically related to Firefox.
We have a set of vimeo embeds, and the ids are pulled in via a json file. On each click, a new video is displayed. After the video is played, the container is removed and the title cloud is put back in. After a certain number of rounds, Firefox performance seriously degrades and you get the "unresponsive script" error. This isn't happening on any other browsers. Furthermore, the profiler in FF doesn't seem to point to a root cause of the slowdown.
I believe this is caused by poor iframe performance and how FF handles iframes, but I'm not entirely sure about this. Nothing else I'm doing is anything too, mostly just stock jquery functions like empty(), remove(), prepend(), etc.
I have implemented a click counter which will just refresh the page after a certain amount of click throughs. This resolved the problem, but it's a hacky solution which I seriously dislike. I would love some ideas on the root cause of this and any advice on how to solve it.
Here's the link to the site and the specific portion mentioned:
http://www.wongdoody.com/mangles
This isn't all the code, but this is the part that gets called every click.
Also, I have tried just swapping out the src="" in the iframe, but performance still degrades.
EDIT: I can confirm this is not a memory leak, I used about:memory and with addons disabled in safe mode I'm getting decent memory usage:
359.11 MB ── private
361.25 MB ── resident
725.54 MB ── vsize
Something in the vimeo embed is slowing down the javascript engine, but it's not a memory leak. Also, this is confirmed by the fact that I can resolve the issue by just refreshing the page. If it was a memory leak I would have to close FF altogether.
function getIframeContent(vid) {
mangle_vid_id = vid;
return '<div class="vimeoContainerflex"><div class="vimeoContainer"><iframe class="vimeo" style="z-index:1;" width="100%" height="100%" frameborder="0" allowfullscreen="" mozallowfullscreen="" webkitallowfullscreen="" src="//player.vimeo.com/video/' + mangle_vid_id + '?api=1&title=0&color=89ff18&byline=0&portrait=0&autoplay=1"></iframe></div></div>';
}
function show_titles() {
$('.mangle-btn').hide();
$('.vimeoContainerflex').remove();
$('span.mangle').hide();
if ($('#mangle-titles').length < 1) {
$('#wongdoody').prepend(wd_titles_content);
}
$('#arrow').show();
if (clicks > 12) {
location.reload();
}
$('#mangle-titles span').click(function() {
clicks = clicks + 1;
$('#mangle-wrapper').remove();
var vidID = $(this).attr('data-id');
if ($('.vimeoContainer').length < 1) {
if (vidID == "home") {
$('#wongdoody').prepend(getIframeContent(getRandom()));
} else {
$('#wongdoody').prepend(getIframeContent(vidID));
}
}
$('#arrow').hide();
vimeoAPI();
});
$('#mangle-titles span').not('noscale').each(function() {
var _this = $(this);
var classname = _this.attr('class');
var scaleNum = classname.substr(classname.length - 2);
var upscale = parseInt(scaleNum);
var addition = upscale + 5;
var string = addition.toString();
_this.hover(
function() {
_this.addClass('scale' + string);
},
function() {
_this.removeClass('scale' + string);
}
);
});
}
function vimeoAPI() {
var player = $('iframe');
var url = window.location.protocol + player.attr('src').split('?')[0];
var status = $('.status');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
} else {
window.attachEvent('onmessage', onMessageReceived, false);
}
// Handle messages received from the player
function onMessageReceived(e) {
var data = JSON.parse(e.data);
switch (data.event) {
case 'ready':
onReady();
break;
case 'finish':
onFinish();
break;
}
}
// Helper function for sending a message to the player
function post(action, value) {
var data = {
method: action
};
if (value) {
data.value = value;
}
var message = JSON.stringify(data);
if (player[0].contentWindow != null) player[0].contentWindow.postMessage(data, url);
}
function onReady() {
post('addEventListener', 'finish');
}
function onFinish() {
setTimeout(show_titles, 500);
}
}
Part of you're problem may be the fact that you keep adding more and more click-handlers to the spans. After each movie ends the onFinish function calls show_titles again, which attaches a new (=additional) click-handler to the $('#mangle-titles span') spans. jQuery does not remove previously attached handlers.
Try splitting the show_titles function into two. init_titles should be called only once:
function init_titles() {
if ($('#mangle-titles').length < 1) {
$('#wongdoody').prepend(wd_titles_content);
}
$('#mangle-titles span').click(function() {
$('#mangle-wrapper').remove();
var vidID = $(this).attr('data-id');
if ($('.vimeoContainer').length < 1) {
if (vidID == "home") {
$('#wongdoody').prepend(getIframeContent(getRandom()));
} else {
$('#wongdoody').prepend(getIframeContent(vidID));
}
}
$('#arrow').hide();
vimeoAPI();
});
$('#mangle-titles span').not('noscale').each(function() {
var _this = $(this);
var classname = _this.attr('class');
var scaleNum = classname.substr(classname.length - 2);
var upscale = parseInt(scaleNum);
var addition = upscale + 5;
var string = addition.toString();
_this.hover(
function() {
_this.addClass('scale' + string);
},
function() {
_this.removeClass('scale' + string);
}
);
});
}
function show_titles() {
$('.mangle-btn').hide();
$('.vimeoContainerflex').remove();
$('span.mangle').hide();
$('#arrow').show();
}
I'd recommend trying to re-use the iframe instead of wiping and re-adding. Failing that, I think you may be out of luck. Your method of closing the iFrame is fine; your browser that it's running in is not.
You're overloading window with eventListeners. Each time a user clicks a video, you're attaching an event to window that fires every time you're receiving a message.
You can easily check this by adding console.log("Fire!"), for instance, at the beginning of onMessageReceived. You'll see that this function gets triggered an awful number of times after the user has performed some clicks on videos.
That surely has an impact on performance.
Hope this helps.
I'm trying to solve a quite simple task but stuck with JQuery behavior.
I have a HTML button which I disable (add disabled attribute) right after it get clicked to prevent multiple clicks, do something long running (i.e. update DOM with a lot of elements) and enable the button back.
Problem is that even the button is disabled jquery queues all clicks on it and raise my click handler right after it became enabled.
According to JQuery docs it should not raise events for a disabled element.
Bellow is my code. Open JS console, click two times on the button, notice couple 'START --' messages in the console.
<div>
<button id="mybtn" type="button">Find</button>
</div>
var btnElement = $('#mybtn');
btnElement.click(doClick);
function doClick() {
var btnElement = $('#mybtn');
btnElement.attr('disabled', true);
console.log('START --' + Date());
for (var i = 0; i < 70000; i++) {
var el = $('#mybtn');
var w = el.width();
w += 10;
}
console.log('STOP --' + Date());
el.attr('disabled', false);
}
Here is my solution http://jsfiddle.net/DRyxd/8/
var btnElement = $('#mybtn');
var buttonIsBusy = false;
function doHeavyJob () {
console.log('START --' + Date());
for (var i = 0; i < 70000; i++) {
var el = $('#mybtn');
var w = el.width();
w += 10;
}
var timeoutId = setTimeout (unblockTheButton, 0);
console.log('STOP --' + Date());
}
function unblockTheButton () {
console.log('unblockTheButton');
btnElement.attr('disabled', false);
buttonIsBusy = false;
}
function doClick() {
console.log('click', buttonIsBusy);
if (buttonIsBusy) {
return;
}
btnElement.attr('disabled', true);
buttonIsBusy = true;
var timeoutId = setTimeout (doHeavyJob, 0);
}
btnElement.click(doClick);
The issue here is that click-handler function has not finished and browser has not refreshed the DOM. That means that block was not yet applied to the button. You can try pushing your heavy code out of the current context like this:
function someHeavyCode () {
/* do some magic */
}
var timeoutId = setTimeout(someHeavyCode, 0);
This will push your heavy code out of the current context.Letting browser to update the DOM first and only after execute the heavy code.
While the heavy code is executed, browser (at least Chrome) kept the user input queue somewhere in other place (or most-likely other thread). And as soon as heavy code completes - it feeds the DOM with all that queued events. We need to ignore that events somehow. And I use the setTimeout with 0-time again. Letting the browser do what was queued before unblocking the button.
WARNING But be extremely careful with this technique. Browser will still be blocked and if you spawn a lot of such blocks it may hang.
See also this Why is setTimeout(fn, 0) sometimes useful? and consider using webworkers.
P.S. Blocking a user input in such a way is not a good approach, try to rethink what you are going to do, probably there is a better solution for that.
I have a function that updates a <div /> via AJAX:
function update() {
<!-- .ajax() -->
setTimeout(update(), 3000);}
}
What I need is that this is not executed when the user is not present on the website, so if there is no movement of the mouse (we will suppose that if move it is in the website) it will not update .mousemove(). By the way, there is any other thing that we can do to know is someone is active on the website?
How can this be done? Thank you in advance!
Edit: probably I explained bad. I need to know the way to only update when there is activity. Like Facebook does with his news feed, the front page. Thanks!
You could use a mousemove handler to track when the user last moved, and then have the process only happen if they last moved the mouse within X seconds. But of course, if the user is sitting there reading something, or if they're a keyboard-oriented kind of person, that will tend to miss that they are there... So you'd probably want to look at keydown as well.
Here's a mousemove example:
jQuery(function($) {
var count = 0, lastmove = new Date();
$(document).mousemove(function() {
++count;
lastmove = new Date();
$('#display').html("Moved " + count + " times");
});
});
Then your update code could do this:
function update() {
if (new Date() - lastmove < 60000) { // 60 seconds
// Really do the update
}
else {
// Check back in a few seconds
setTimeout(update, 3000);
}
}
Off-topic, but you have an error in your update code. You have:
setTimeout(update(), 3000);
...which will call update immediately and then try to use its return value to schedule something to happen in three seconds. If you want the call to update to be scheduled to happen in three seconds, leave off the () after it:
setTimeout(update, 3000);
I think I might have ended up with something such as this. Avoids date arithmetic. Only cares whether there's been some activity since the last update().
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
}
window.setTimeout(update, updateDelay);
edit: I've discovered a flaw in the code. The following is more appropriate:
window.activeFlag = false;
window.updateDelay = 3000;
$(document).bind('mousemove scroll keydown', function(){ activeFlag = true; });
function update() {
if(activeFlag) {
doWork();
activeFlag = false;
}
window.setTimeout(update, updateDelay);
}
update();
I think there is no easy way to determine if the user is present
I would use a combination of mousemove, scroll, keypress.
var bUpdate = false;
function update() {
if(bUpdate){
///perform your ajax request
}
}
$(document).mousemove(function(){
bUpdate = true;
setTimeout(function(){bUpdate=false;}, 3000);}
});