I'm trying to make a mobile version of my site, and the jplayer functionality closely resembles that of my main site. The playlist is updated depending on which page you are on, and all the songs except the one you are currently listening to are deleted from the playlist, then the page dynamically adds songs from the page you are viewing.
It works fine on my main page, but my mobile version (which is almost the same code), does not work on.
I set jplayer_playlist.option("removeTime", 0); just like the jplayer documentation suggests, but it doesn't work. Here is a bit of my code so you can see exactly what I'm doing.
function reload()
{
var current = jplayer_playlist.current;
for(var i = 0; i < current; i++)
{
deleteSong(0);
}
var length = theTitles.length;
for(var i = 0; i < (length - 1); i++)
{
deleteSong(1);
}
}
function deleteSong(index)
{
if(!jplayer_playlist.remove(index))
{
console.log("Could not delete it.");
}
}
The first delete does not show the error message, but the second (and every delete after) does. It seems as though it is not recognizing that I set the removeTime to 0, even though I did (and before any delete calls were made). Is there anything else that the jplayer.remove function depends on when you are trying to delete something from it besides removeTime?
I had the same problem and found an answer in jPlaylist docs: http://jplayer.org/latest/demo-02-jPlayerPlaylist/
"Because the second command will only work if the remove animation time, removeTime, is zero."
In my case, I coded the following feature to erase all the songs up to the current one:
When creating the jPlaylist:
var playlistOptions = {
playlistOptions: {
enableRemoveControls: true,
autoPlay: false,
removeTime: 0
},
...
};
playlist = new jPlayerPlaylist(.......);
And the function that erases the songs:
function deleteUpToCurrent(e) {
while(playlist.current != 0) {
playlist.remove(0);
}
return false;
}
Hope it helps!
Cheers
On line 355 of jquery.playlist.js in the jplayerPlaylist.remove function, there is a call to JQuery's remove function:
$(this).remove();
If you look in the source of JQuery, this is delegated to the DOM method
elem.parentNode.removeChild( elem );
In modern browsers, this DOM operation is asynchronous, i.e. it returns immediately but actually removing the DOM element takes time. Therefore the jplayerPlaylist.remove() method returns before the node has actually been removed. Unfortunately, there is no callback. So the only workaround is a setTimeout.
var DELAY = 10;
function reload()
{
var current = jplayer_playlist.current;
var length = jplayer_playlist.playlist.length;
if (current > 0)
deleteSong(0);
else if (length > 1)
deleteSong(1);
if (length > 1)
window.setTimeout(reload, DELAY);
}
You may have to adjust DELAY.
Related
I thought an interval just delayed the function, but as it turns out it actually loops.
When I include some function that stops the interval after the deletor function ends it doesn't trigger that and I still get Test logged to the console.
document.addEventListener("DOMContentLoaded", function(event) {
let fullURL = window.location.href;
//let fullURL2 = window.location.host + window.location.pathname;
if (fullURL === "https://net.adjara.com/" ||
fullURL === "https://net.adjara.com/Home") {
var timer = setInterval(deletor, 5);
function deletor() {
timer;
var slider = document.querySelector("#slider-con");
var bannerTop = document.querySelector("#MainContent > div:nth-child(2)")
var bannerMiddle = document.querySelector("#MainContent > iframe");
var bannerRandom = document.querySelector("#MainContent > div:nth-child(3)");
if (slider) {
slider.parentNode.removeChild(slider);
}
if (bannerTop) {
bannerTop.parentNode.removeChild(bannerTop);
}
if (bannerMiddle) {
bannerMiddle.parentNode.removeChild(bannerMiddle);
}
if (bannerRandom) {
bannerRandom.parentNode.removeChild(bannerRandom);
}
function stopInterval() {
clearInterval(timer);
}
console.log("Test");
/*if ()
clearInterval(timer);*/
};
} else {
return false;
}
});
What you're looking for is setTimeout. It runs only once.
setTimeout(deletor, 5);
Also, you don't need to write timer variable inside of your closure like you would in Python. Javascript captures everything that's inside of lexical scope.
The code you provided works ¯\_(ツ)_/¯
Maybe the problem is coming from what triggers stopInterval()
But as mentioned in comments / other answers, you might be better off with another method
I wouldn't recommend using setTimeout in your case, because it looks like you are simply waiting for some DOM elements to be loaded. The problem with the timeout is that you can't know for sure how fast is the computer that will run your code. Maybe a bad quality phone with an outdated software that will need way more time to run your code than when you test on your personal computer, and that will not have the elements loaded by the time your function will be executed.
jQuery
For this reason, and since you tagged your question with jQuery I think you could use $(elementYouWaitForTheDOM2beLoaded).ready(function2execute) for each element you are watching instead of a having a loop that waits for the elements to be loaded (documentation for "ready" function)
Vanilla JS
And if you want to do it in pure JS it would be document.querySelector(elementYouWaitForTheDOM2beLoaded).on('load', function2execute))
I am working on a User script but it seems to work just for the first comparison:
(function() {
'use strict';
var noError = document.getElementsByClassName("noMistakeButton");
var next = document.getElementsByClassName("nextButton");
var wait = document.getElementsByClassName("understoodButton");
var okko = document.getElementsByClassName("buttonKo");
var exitOkko = document.getElementsByClassName("exitButton");
while(1)
{
if( noError !== null)
{
noError.click();
}
if( next !== null)
{
exit.click();
}
if( wait !== null)
{
wait.click();
}
sleep(2);
if( okko !== null)
{
exit.click();
}
if( exitOkko !== null)
{
exitOkko.click();
}
} })();
My goal is the run this script freely while AFK.
There are, as you can see, many buttons on the web page and each button cant be :visible or :hidden. My goal is just to click on them.
Here is the target page (static URL).
Some buttons only have class and no ID. Others have them both.
The console reports:
ERROR: Execution of script 'AutoVoltair' failed! noError.click is not
a function
It is unclear what you hope to accomplish. If you are trying to step through a sequence of controls, use the approach illustrated in Choosing and activating the right controls on an AJAX-driven site.
The kind of code shown in the question would just play "Whac-A-Mole" with whatever button "popped up" next. (And only if the preceding buttons had been deleted.)
Anyway, to answer the question: "why this code is only doing the first if?".
It's because userscripts (and javascript) stop running at the first critical error (with a few exceptions). Additionally:
noError.click is not a function because noError is a collection of elements, not a button.
All the getElementsByClassName calls are only done once. If it's going to continually loop, you need to recheck inside the loop.
There is no such thing as sleep().
while (1) is a very bad idea and can freeze your browser, or even help crash your PC. Use setInterval for polling the page.
Here's the "Whac-A-Mole" code with those errors corrected:
setInterval ( () => {
var noError = document.getElementsByClassName ("noMistakeButton");
var next = document.getElementsByClassName ("nextButton");
var wait = document.getElementsByClassName ("understoodButton");
var okko = document.getElementsByClassName ("buttonKo");
var exitOkko = document.getElementsByClassName ("exitButton");
if (noError.length) {
noError[0].click ();
}
if (next.length) {
next[0].click ();
}
if (wait.length) {
wait[0].click ();
}
if (okko.length) {
okko[0].click ();
}
if (exitOkko.length) {
exitOkko[0].click ();
}
},
222 // Almost 5 times per second, plenty fast enough
);
If you want to sequence clicks, use chained waitForKeyElements() calls as shown in this answer.
I have a search box that hides all lines in a list that don't contain the entered text.
This worked great until the list became 10,000 lines long. One keystroke is fine but if the user types a several letter word, the function is iterated for each keypress.
What I want to do is to abandon any previous execution of the function if a new key is pressed.
The function is very simple, as follows:
$("#search").keyup(function(e) {
goSearch();
});
function goSearch()
{
var searchString = $("#search").val().toLowerCase();
$(".lplist").each(function(index, element) {
var row = "#row-" + element.id.substr(5);
if ($(element).text().toLowerCase().indexOf(searchString,0) != -1)
$(row).show();
else
$(row).hide();
});
}
Thanks
You can't directly. Javascript is not multi-threaded so your function will run and block any key-presses until it is done.
The way this is made tolerable from a user-experience point of view is to not trigger a function immediately on a key event, but to wait a short period of time and then fire the event.
While the user is typing, the timeout function will continually be set and reset and so the gosearch function won't be called, and so the user won't have their typing interrupted.
When the user pauses typing, the timeout will countdown to zero and call the search function, which will run and block typing until it completes. But that's okay (so long as it completes within a second or so) as the user is probably not currently trying to type.
You can also do what you actually asked by breaking up your gosearch function into chunks, where each call to the function: * Reads a counter of the number of lines processed so far, and then processes another 500 lines and increments the counter. * Calls another gosearch using setTimeout with a value of zero for the time. This yields events to other 'threads', and allows for fast changing of search terms.
var goSearchTimeout = null;
var linesSearched = 0;
function keySearch(e){
if(goSearchTimeout != null){
clearTimeout(goSearchTimeout);
linesSearched = 0;
}
goSearchTimeout = setTimeout(goSearch, 500);
}
$("#search").keyup(keySearch);
function highLight(index, element) {
if(index >= linesSearched){
var row = "#row-" + element.id.substr(5);
if ($(element).text().toLowerCase().indexOf(searchString,0) != -1){
$(row).show();
else{
$(row).hide();
}
if(index > linesSearched + 500){
linesSearched = index;
goSearchTimeout = setTimeout(goSearch);
return;
}
}
function goSearch(){
goSearchTimeout = null;
var searchString = $("#search").val().toLowerCase();
$(".lplist").each(highLight);
}
If you're going to use timeout callbacks like this, I'd strongly recommend wrapping your code up into jQuery widgets, so that you can use variables on the object to store the variables goSearchTimeout etc rather than having them float around as global variables.
Introduce a counter var keypressCount that is being increased by your keypress event handler. at the start of goSearch() write its value into a buffer. Then at each run of your $(".lplist").each() you ask if the current keypressCount is the same as the buffered one; if not, you return. I would suggest you use a for() though since it is easier to break; than $.each().
Update:
You will have to make sure that there is time for new keypress events to be fired/received, so wrap the anonymous function of your $.each() inside a timeout.
Reference: http://www.garrickcheung.com/javascript/what-i-learned-about-multi-threading-in-javascript/
You can use a global variable to save search string and stop execution when search string changes.
IMPORTANT: You must set a timeout in each iteration so that function execution is paused and global variables are updated, as JavaScript is single-threaded.
Your code would look like this:
var searchString;
$("#search").keyup(function(e) {
// Update search string
searchString = $("#search").val().toLowerCase();
// Get items to be searched
var items = $(".lplist");
// Start searching!
goSearch(items, searchString, 0);
});
function goSearch(items, filter, iterator)
{
// Exit if search changed
if (searchString != filter) {
return;
}
// Exit if there are no items left
if (iterator >= items.length) {
return;
}
// Main logic goes here
var element = items[iterator];
var row = "#row-" + element.id.substr(5);
if ($(element).text().toLowerCase().indexOf(filter, 0) != -1)
$(row).show();
else
$(row).hide();
// Schedule next iteration in 5 ms (tune time for better performance)
setTimeout(function() {
goSearch(items, filter, iterator + 1);
}, 5);
}
I would like to animate an html page with something like this:
function showElements(a) {
for (i=1; i<=a; i++) {
var img = document.getElementById(getImageId(i));
img.style.visibility = 'visible';
pause(500);
}
}
function pause(ms) {
ms += new Date().getTime();
while (new Date() < ms){}
}
Unfortunately, the page only renders once javascript completes.
If I add
window.location.reload();
after each pause(500); invocation, this seems to force my javascript to exit. (At least, I do not reach the next line of code in my javascript.)
If I insert
var answer=prompt("hello");
after each pause(500), this does exactly what I want (i.e. update of the page) except for the fact that I don't want an annoying prompt because I don't actually need any user input.
So... is there something I can invoke after my pause that forces a refresh of the page, does not request any input from the user, and allows my script to continue?
While the javascript thread is running, the rendering thread will not update the page. You need to use setTimeout.
Rather than creating a second function, or exposing i to external code, you can implement this using an inner function with a closure on a and i:
function showElements(a) {
var i = 1;
function showNext() {
var img = document.getElementById(getImageId(i));
img.style.visibility = 'visible';
i++;
if(i <= a) setTimeout(showNext, 500);
}
showNext();
}
If I add window.location.reload(); after each pause(500) invocation, this seems to force my javascript to exit
window.reload() makes the browser discard the current page and reload it from the server, hence your javascript stopping.
If I insert var answer=prompt("hello"); after each pause(500), this does exactly what I want.
prompt, alert, and confirm are pretty much the only things that can actually pause the javascript thread. In some browsers, even these still block the UI thread.
Your pause() function sleeps on the UI thread and freezes the browser.
This is your problem.
Instead, you need to call setTimeout to call a function later.
Javascript is inherently event-driven/non-blocking (this is one of the great things about javascript/Node.js). Trying to circumvent a built in feature is never a good idea. In order to do what you want, you need to schedule your events. One way to do this is to use setTimeout and simple recursion.
function showElements(a) {
showElement(1,a);
}
function showElement(i, max) {
var img = document.getElementById(getImageId(i));
img.style.visibility = 'visible';
if (i < max) {
setTimeout(function() { showElement(i+1, max) }, 500);
}
}
var i = 1;
function showElements(a) {
var img = document.getElementById(getImageId(i));
img.style.visibility = 'visible';
if (i < a) {
setTimeout(function() { showElements(a) }, 500);
}
i++;
}
showElements(5);
function showElements(a,t) {
for (var i=1; i<=a; i++) {
(function(a,b){setTimeout(function(){
document.getElementById(getImageId(a)).style.visibility = 'visible'},a*b);}
)(i,t)
}
}
The t-argument is the delay, e.g. 500
Demo: http://jsfiddle.net/doktormolle/nLrps/
I have a while loop:
x = true;
while (x == true) {
document.images['id'].src = arr[i];
i = i+1;
x = confirm('do you want to see more?')
}
This shows me each image and then asks if I want to see more on firefox and ie, but in chrome and safari, it only displays the image after I leave the loop. I know this is efficient, but I'm wondering if there's a way to force execution of the line within the loop as I go along?
Thanks for input!
You can add a sequence of setTimeout instead of a loop to force the javascript user thread to stop and thus let the browser refresh the drawing.
var i = 0; // define outside showNextImage to be properly captured by it.
var showNextImage = function() {
document.images['id'].src = arr[i];
i = i+1;
x = confirm('do you want to see more?');
if (true) setTimeout(showNextImage, 10);
};
Two answers:
Don't use confirm
If you really want to use confirm, yield to the browser after updating the image but before the confirm
1. Don't use confirm
The best way is to not use confirm at all; it's antiquated and as you've found it behaves slightly differently on different browsers in terms of whether changes to the page are shown.
Instead, I'd use any of the 350,124 "dialog" libraries that are out there (jQuery UI has a nice one, but again, there are a lot of them), which work asynchronously and so you definitely see the page changes. Your loop would become an asynchronous function, but those aren't all that tricky once you're used to them and the benefits are enormous in terms of the user experience.
function chooseImage(arr, completionCallback) {
var i = 0, imgElement = document.images['id'];
ask();
function ask() {
imgElement.src = arr[i];
showDialog(gotAnswer); // the nature of "show dialog" will depend on which one you use
}
function gotAnswer() {
if (userSaidYes) { // Again, depends on the library you're using
completionCallback(i); // Tell the calling code which one they picked
}
else {
// Is there another?
++i;
if (i >= arr.length) {
// No, tell the user
/* left as exercise */
// Tell calling code none was chosen
completionCallback(-1); // Using -1 as a flag for none
}
else {
// Yes, ask about it
ask();
}
}
}
}
2. Use confirm but yield
The issue is that confirm brings things to a screeching halt while the browser asks the user a question. Changes you've made to the page may not show up while the confirm window is active (as you've seen).
If you really want to use confirm, you can still do that, just yield back to the browser briefly first so that it has time to show the page changes. Note, though, that this still may not be a guarantee, if the image takes a long time to download.
function chooseImage(arr, completionCallback) {
var i = 0, imgElement = document.images['id'];
showAndHandOff();
function showAndHandOff() {
imgElement.src = arr[i];
setTimeout(ask, 0);
}
function ask() {
if (confirm('do you want to see more?')) {
++i;
if (i >= arr.length) {
alert("Sorry, there aren't any more.");
completionCallback(-1);
}
else {
showAndHandOff();
}
}
else {
completionCallback(i);
}
}
}
For example:
var x = true,
i = 0,
fn = function() {
document.images['id'].src = arr[i];
x = confirm('do you want to see more?');
if ( x ) {
i = i+1;
setTimeout(fn, 0)
}
};
fn();