I have the following jQuery code in my page onload function:
$("#vid-pages").find("video").on('canplay', function() {
$(this)[0].currentTime = $(this)[0].duration / 2;
console.log($(this)[0].currentTime);
});
There are only two videos in that container, and none anywhere else on the page. When I check the console, it's continuously flooded with the time returned in that code block. What is the solution to make this trigger only once, instead of constantly?
When the current time is changed the browser needs to load more data either from cache or the network. This can trigger the canplay event. And since a time is set in the event handler you will get a never-ending loop (you can see the effect of canplay being triggered here by choosing a video, hit play then skip to the middle right after). It may depend on the browser.
This page on MDN states the following to the related canplaythrough (though not entirely the same it is reasonable to believe this also applies to canplay as shown in the media event page using Firefox):
Note: Manually setting the currentTime will eventually fire a
canplaythrough event in firefox. Other browsers might not fire this
event.
To avoid either unsubscribe from the event, or use a flag which forces exit at the second time the event is triggered.
var initialPlay = false;
$("#vid-pages").find("video").on('canplay', function() {
if (initialPlay) return;
initialPlay = true;
$(this)[0].currentTime = $(this)[0].duration / 2;
console.log($(this)[0].currentTime);
});
For unsubscribing you would need to use a non-anonymous function.
Related
Hi I am using this small script to create cuepoints for my video:
(function(a){a.fn.cuepoints=function(c){var e=this[0];var d={};function b(){d=a.extend({},c)}b();e.addEventListener("playing",b);e.addEventListener("ended",b);e.addEventListener("loadstart",b);e.addEventListener("seeked",b);e.addEventListener("timeupdate",function(){for(var g in d){var f=parseFloat(g,10);g=g+"";if(f<=e.currentTime&&e.currentTime<=f+0.5){d[g]();delete d[g]}}});return this}})(jQuery);
The problem I am facing is my button will play the video on the first click just fine and when the cuepoint is reached the video pauses however when I press the play button a second time the video will not play (even though the console confirms it has been clicked twice) but if I click the button a third time the video will play again any ideas?
Here's the code:
var vid = document.getElementById("video");
$(document).on('click touchstart tap', '.nextSlide', function() {
vid.play();
console.log('Clicked');
});
$('#video').cuepoints({
10.5: function () {
vid.pause();
},
15: function() {
}
});
Here's what's happening:
The $.cuepoints() plugin keeps track of all the times and callbacks in an object: {10.5: function() {...}, ...}. Whenever the video's timeupdate event fires, the plugin looks at the object, checks to see if the current time is within half a second range of any of the callback times. If so, the associated function is executed and the entry is removed.
However, if someone were to rewind the video, there has to be a way to run those functions at the specified times again. To handle that, the plugin listens for playing, ended, loadstart, and seeked events. When any of those events occur, the time and callback object gets reset.
So here's your problem: when you run vid.pause() in your callback function, the video stops. When the user unpauses video, the playing event fires, resetting all the callbacks. Almost immediately, a timeupdate event realizes that you're still within the half second period, and calls your callback function yet again.
In point of fact, it doesn't always play on the third click. Sometimes it takes four clicks, because the timeupdate event keeps firing inside that half-second period.
Now, the half-second range is necessary. You don't get any guarantee of how often the timeupdate is going to fire, so if you don't have something like that, you run a good chance of skipping the callbacks altogether.
My solution is to stop listening to playing events. The time/event object gets set up when the plugin is originally called, so the first play work fine. When users use the meter to move to a different time, the seeked event will fire. When users get to the end and decide to replay it, the ended event will already have fired. I don't think that the playing event is necessary.
Here's a JSFiddle demonstrating the fix: https://jsfiddle.net/zxfzjqu2/
I'm using the beforeunload event to as a means to proxy into metrics about clicks on my site. For example, if beforeunload fires within ~50ms of a click event, I can determine when a user has selected a link.
This gets complicated with iOS, as it doesn't support beforeunload. As per Apple docs, I've substituted pagehide for beforeunload, but this doesn't have the same functionality. It looks as if pagehide replaces unload - NOT beforeunload. In my tests their time to fire differs by ~1500ms on average (pagehide triggers much later than beforeunload). This makes attributing pagehide events to clicks very difficult.
Is there any event supported by safari on iOS that can get close to the trigger time of beforeunload?
NOTE: I know this isn't ideal, and there are other, better approaches which I will likely end up pursuing, but I was still curious about this.
Theoretically pagehide is replacement for beforeunload in safari but practically its not. As correctly observed time to trigger pagehide event can vary because safari fetches the new page in the background and when page is available it fires pagehide.
There is no event if you are looking for one but there is this hack which you can try, as soon as click happens safari stop running requestAnimationFrame. So you can try something like this, as click happens record clickTs and start recording timestamp inside requestAnimationFrame function, now when pageHide fires you just check the time difference between clickTsand eventlastReqAnimTime, it should be roughly between 500 ms. This way you can attribute click to pageHide
var w = window,
reqAnim = null,
clickTs = 'record this when click happens',
eventlastReqAnimTime = +new Date,
logReqAnim = function() {
eventlastReqAnimTime = +new Date;
reqAnim = w.requestAnimationFrame(logReqAnim)
};
reqAnim = w.requestAnimationFrame(logReqAnim);
setTimeout(function () {
w.cancelAnimationFrame(reqAnim);
}, 1500)
w.addEventListener('pagehide', function(){
if(Math.abs(clickTs - eventlastReqAnimTime) < 500 ){
console.log('your link was chosen')
}
})
NOTE: above code is just pseudo, test before running.
I have come accros this problem before. This time it is an HTML5 color input giving the problem. Here's the issue. I want to use an onChange event to send the user selected value from a color input to the server via Ajax - for an almost real time update to a database. However, when the user drags his mouse over the color swatch, it will trigger loads of onChange events in rapid succession, for each value change, so the server will get inundated with hundreds of pointless requests until the user stops sliding their mouse!
I could use onMouseOut for this particular issue, but I have had this issue with other things like detecting browser window resizing when a user click/drags to resize and the event is triggered with every pixel change. So I'm looking for an answer that will trigger on the final value only, for all similar scenarios.
What is the best method for dealing with this and just triggering when the data has finished changing. I.e. ignore every change until user has settled on a final value. An onAfterChange method event would be nice, but I can't find one!
You can prevent this by debouncing your event handler function.
Every time a debounced function is called, it "pushes back" its execution by nms. This means that while triggered numerous times within t ms (eg: while the mouse is moving), it will only execute the handler after not being triggered for t ms (eg: when the mouse has stopped moving).
Edit:
Ok, the jquery link was a bit out-of-date.. a better example of how to debounce can be found in the Underscore.js source here.
How about this:
Set a timeout to send request to server, after the first onChange triggering, and change the first flag to false.
Every onChange is triggered, reset the timeout count.
After sending request, reset the first flag.
Edit:
This is the sample code:
$(document).ready(function(){
var myInterval = undefined;
var mytimeoutcount = NaN;
var i =0; // for debug
$("#myinput").keyup(function(){
console.log("event trigger"); // for debug
mytimeoutcount = 300;
if(myInterval == undefined){
myInterval = setInterval(function(){
mytimeoutcount -= 10;
if(mytimeoutcount <= 0){
// do request, for debug
i+=1;
console.log(i);
window.clearInterval(myInterval);
myInterval = undefined;
}
}, 10);
}
});
});
#myinput{
width:100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input id="myinput" type="text" value="test" />
I develop an app using phonegap and jquerymobile/jquery.
During development I only had a virtual iOS device and now since I'm testing the app on a real device I see, that time between click on an element and handle this event is very long.
E.g if i Click on an icon a loader icon is shown but this icon first come in the moment the next page is final loaded (a very short time the loader is shown).
I develop with Javascript since a long time and always have focus on performant execution but this is very strange.
The App has got about 10 views in one HTML file. And on click on an element only show the next part of these file.
Does anyone know about solutions to solve "Bugs" like these?
Thanks in advance.
The click delay on iPhones is a feature used to distinguish between clicks and scrolls. When you bind to the click event iOS waits approximately 300ms to decide whether you were clicking an object or trying to scroll the page.
You can use jQuery Mobile's vclick event which fires much faster however you will probably run into situations where the vclick event is fired off twice in a row which can result in multiple elements being clicked. Here is some sample code of how to use vclick events and only capture the event triggered first:
$(function () {
//setup a function to check if a vclick event has fired within the last 500ms
function check_vclick () {
//if a vclick event has fired in the last 500ms then return false
if (do_vclick == false) return false;
//otherwise set a flag to disallow vclicks for 500ms
do_vclick = false;
//setup a timeout to allow vclicks in 500ms
setTimeout(function () {
do_vclick = true;
}, 500);
//return true so the event handler knows it's ok to run its code
return true;
}
//setup a flag to allow/disallow vclick events from firing
var do_vclick = true;
//bind an event handler to the vclick event for an element
$('#link_id').bind('vclick', function () {
if (check_vclick()) {
//run the code associated with the element, if it's a link referencing a pseudo-page on the same HTML document, you can do something like this
$.mobile.changePage($(this.href));
}
});
});
Here's a link to the documentation for $.mobile.changePage(): http://jquerymobile.com/demos/1.0rc2/docs/api/methods.html
Here's a link to the documentation for vclick (notice the notes under the virtual mouse event section): http://jquerymobile.com/demos/1.0rc2/docs/api/events.html
Is it possible to determine whether a user is active on the current web page or, say, focused on a different tab or window?
It seems that if you switch tabs, any JavaScript set on a timeout/interval continues running. It would be nice to be able to 'pause' the events when the user is not on the page.
Would something like attaching a mouseover event to the body work, or would that be too resource-intensive?
You can place onfocus/onblur events on the window.
There's wide support for those events on the window.
Example: http://jsfiddle.net/xaTt4/
window.onfocus = function() {
// do something when this window object gets focus.
};
window.onblur = function() {
// do something when this window object loses focus.
};
Open Web Analytics (and perhaps some other tracking tools) has action tracking
You could keep an alive variable going using mousemove events (assuming the user does not leave the mouse still on the page). When this variable (a timestamp likely) has not been updated in x seconds, you could say the page is not active and pause any script.
As long as you do not do a lot of processing in the body event handler you should be okay. It should just update the variable, and then have a script poll it at a certain interval to do the processing/checks (say every 1000ms).
Attach listeners to mousemove, keyup and scroll to the document.
I use this throttle/debounce function (which works without jQuery, even though it's a jQuery plugin if jQuery is present) to only run code in response to them once in ~250ms, so that you're not firing some code on every pixel of the mouse moving.
You can also use the visibilityState of the document:
document.addEventListener("visibilitychange", function() {
if( document.visibilityState === 'visible' ) {
// Do your thing
}
});
There is a wide acceptance of this API.