Toggle Embed Loop - javascript

I'm trying to figure out a way to be able to have an embedded object loop or no longer loop after current play, by clicking a link/image. I need it to NOT change the page, so the current song keeps playing.
So far, I've tried using a Javascript GetElementByName to set loop=true/false, while it's default is currently set to true.
<a href="javascript:void(0)" title="To be implimented"><IMG ID=mode SRC=repeat.gif HEIGHT=22 onclick="PlayerMode()">
<script type="text/javascript">
var L=0;
function PlayerMode()
{
if(L==0){
document.getElementById("playmode").loop="true"
document.getElementById("mode").src="repeat.gif";
L++;
}
else{
document.getElementById("playmode").loop="false"
document.getElementById("mode").src="repeatoff.gif";
L--;
}}
</script>

If you want a link to trigger JavaScript but not change the page afterwards, make sure your onclick code returns false. Also using a simple '"#"' as the link's href will avoid any undesired effects or error messages in case JavaScript is disabled or not supported (for whatever reason).
...
As an alternative, you could as well keep your code and let your function call return false; at the end (see last snipped as well):
...
Some further optimization, I'd use the following code (saves you using a global variable):
function toggleLoop() {
var player = document.getElementById('playmode');
document.getElementById('mode').src = (player.loop = !player.loop) ? 'repeat.gif' : 'repeatoff.gif';
return false;
}
Note that in your current code L should have an initial value of 1 considering the player loops already. Otherwise you'd essentially enable looping on your first click (which won't change anything).

Related

Local Storage may be conflicting with Toggle Slow

I'm trying to have .child_box class to open and close slow but seems Local Storage is not respecting it. Either it wont open or it wont close. Without Local Storage, it works fine. Stumped.
The js:
$("document").ready(function () {
$(".manualclose").click(function () {
$(".child_box").toggle();
});
ls = localStorage.getItem('on')
if(ls) {
$(".child_box").show("slow")
}
$(".open_child").click(function () {
localStorage.setItem('on',true)
toggled = $(".child_box").toggle();
if(toggled.is(":hidden")) {
localStorage.clear();
}
});
$(".manualclose").click(function() {
localStorage.clear();
$(".child_box").hide("slow")
});
});
The button:
<div class="open_child" title="', $txt['sub_boards2'], '">
<i class="fas fa-plus-circle"></i>
</div>
localStorage isn't conflicting with toggle(). The problem is down to the way the browser schedules a reflow whilst executing JavaScript.
In this event handler
$(".open_child").click(function(){
localStorage.setItem('on',true)
toggled = $(".child_box").toggle(500);
if(toggled.is(":hidden")){
localStorage.clear();
}
});
your code toggles the .child-box element. It immediately goes to see if that element is now hidden.
The browser is running the animation that is caused by .toggle() and carries on executing the JavaScript. It checks whether the element is hidden, which it isn't because the animation hasn't completed yet, and so doesn't clear the localStorage. Only later when the animation completes would the element appear as 'hidden'.
You need to do things in a different order:
$(".open_child").click(function(){
let hidden =$(".child_box").is(":hidden");
if (hidden) {
$(".child_box").show(500);
localStorage.setItem('on',true)
} else {
$(".child_box").hide(500);
localStorage.removeItem('on');
}
});
This version checks the hidden status first, then shows or hides the element as required, and updates localStorage to match.
There is an alternative approach: use the complete function available to the jQuery .toggle() method to update localStorage. You'd still need to check to see what .toggle() has just done, so you don't gain much.
FWIW, I never use .toggle() precisely because I don't know what action it's performing.
A couple of other thoughts:
You're not declaring the variable you use, so they're being placed in the global context. This is a bad idea. Declare them in the functions they're used in with let.
localStorage stores strings, not other data type. JavaScript has coerced the data for you so you've got away with it, but good practice suggests that you should be more rigorous.
Using localStorage.clear() precludes the use of localStorage for any other purpose. Use localStorage.removeItem() instead.
I don't think it's localStorage. what i noticed:
a) you don't declare the variable ls.
b) why are you using a div as a button?
c) you don't use parse and stringify to get and set values in the localStorage.
d) you put a title in a DIV container
f) the title you set looks like PHP. it is missing the < ?php echo $text ... ;? > Tags

javascript: remove/add class function

i found a very simple javascript function for a slideshow (here).
It works perfectly, but i want to change something about the control buttons. Right now, the play/pause button displays it's own name, like this:
<button class="controls" id="pause">Pause</button>
The javascript is set to run a function called pauseSlideshow under certain circumstances which not only pauses/resumes the slideshow, but also changes the displayed content of the button from "pause" to "play":
var playing = true;
var pauseButton = document.getElementById('pause');
function pauseSlideshow() {
pauseButton.innerHTML = 'Play';
playing = false;
clearInterval(slideInterval);
}
now, what i want to do is to leave the button empty and work width background images, and to do that i planned to do a simple class swap whenever the pauseSlideshow function is triggered. So, first i added a class called pause to the button with the desired background image set in the style:
<button class="controls pause" id="pause"></button>
and next i was going to do something like this:
var playing = true;
var pauseButton = document.getElementById('pause');
function pauseSlideshow() {
pauseButton.removeClass('pause');
pauseButton.addClass('play');
playing = false;
clearInterval(slideInterval);
}
(where play is another class with a different background image). Sufficient to say, as soon as pauseSlideshow runs, the function crashes. I'm writing something wrong or in the wrong place, but i know that the approach is viable.
please, can anybody point out the error?
EDIT:
Use $(pauseButton).removeClass('pause'); and
$(pauseButton).addClass('play');. addClass and removeCalss are jQuery
methods. And you need to add jQuery reference too. – user3698428
document.getElementById("pause") does not return a jQuery element.
.removeClass() and .addClass() are jQuery functions. So basically you
are trying to call jQuery functions on a non-jQuery element and it's
throwing an error. Change it to var pauseButton = $("#pause"); or
$(pauseButton).removeClass(...); $(pauseButton).addClass(...);
when i try either way, console returns: Uncaught ReferenceError: $ is not defined
When working in pure javascript, you should use this to add or remove class:
pauseButton.classList.remove('pause');
pauseButton.classList.add('play');
https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Limiting click detection on the page [javascript]

I'm trying to limit the user's ability to click on an object to a certain time limit. I looked around and found that apparently, setTimeout() is the correct function to use for this type of thing. I've applied the function to my code, but its not working. I'm thinking/know now that the problem is that the setTimeout in my code isn't limiting the actual click event, which I need to do. Here is a snippet of my click code:
function clickRun(event) {
var $objectVersion = correspondingObject(event.target.id);
if (isAnyVisible() == false) { // none open
$objectVersion.makeVisible();
} else if (isAnyVisible() && $objectVersion.isVisible()) { //click already open div
$objectVersion.makeInvisible();
} else if (isAnyVisible() && $objectVersion.isVisible()==false) { //different div open
searchAndDestroy();
$objectVersion.delay(600).makeVisible();
};
};
$('.ChartLink').click(function(event) {
setTimeout(clickRun(event),5000);
});
I've also created a JSFiddle to represent what I'm talking about: http://jsfiddle.net/FHC7s/
Is there a way to achieve limiting the actual click detection on the page?
I think the easiest way to do it is to keep track of the time of the previous click and if the current click is too soon after that, then don't do anything:
onClick = function(){
if(new Date().getTime() - lastCheck < MIN_CLICK_SPACING) return;
}
Have a look at this JSFiddle, I've set it up so you can have the button disable itself for time duration after detecting a click. Just make sure to remember how your closures are operating with your setTimeouts.
Your code contains an error... your line should be
setTimeout(function(){clickRun(event)},5000);
but even then I don't think that's exactly what you're looking for; that code will "delay" the click by 5 seconds, not actually prevent more clicks. If your true intent is to ignore all clicks after a certain amount of time, then I would go with mowwalker's answer; there's no way to stop the clicks, but you can check to see if you should honor them or not.

addEventListener not working after innerHTML

I'm writing a simple text-base game in my free time. It's called TARDIS flight (Doctor Who!) and I'm working on the main menu.
So I'm using a function, addMainMenuListeners, to add all the event listeners with addEventListener, after I set the innerHTML of the main menu.
Everything works fine, until the point where I go back to the main menu from one of the submenus. Then, I found that the buttons don't work anymore.I'm calling addMainMenuListeners after I set the innerHTML, but even though I do it, and I do it in the console, and I check, there is no event.
Code:
In my main javascript file:
function addMainMenuListeners()
{
if($("start")) $("start").addEventListener("click", startGame);
if($("instructions")) $("instructions").addEventListener("click", instructions);
if($("settings")) $("settings").addEventListener("click", settings);
if($("back")) $("back").addEventListener("click", resetMainMenu);
if($("volume")) $("volume").addEventListener("change", function(){saveAndChangeVolume($("volume").value);});
}
function instructions()
{
$("mainmenu").innerHTML = "<h1>Instructions</h1><p>Fly your TARDIS through the time vortex using the console. Make sure that you use the correct materialization codes. Try to keep the time-space coordinates close to the values of the ones given. AND DON'T CREATE PARADOXES, WHATEVER YOU DO!</p><button id='back'>Back</button>";
addMainMenuListeners();
return true;
}
function settings()
{
$("mainmenu").innerHTML = "<h1>Volume</h1><span>1</span><input type='range' id='volume' min='1' max='100' /><span>100</span><br><button id='back'>Back</button>";
addMainMenuListeners();
loadVolume();
return true;
}
function resetMainMenu()
{
$("mainmenu").innerHTML = "<h1>TARDIS Flight</h1><button id='start'>Start!</button><button id='instructions'>Instructions</button><button id='settings'>Settings</button>";
addMainMenuListeners();
return true;
}
And in my HTML file:
<div id="mainmenu">
<h1>TARDIS Flight</h1>
<button id="start">Start!</button>
<button id="instructions">Instructions</button>
<button id="settings">Settings</button>
</div>
If you need any clarification, just ask.
EDIT: Evidentally, nobody got what I meant. I was readding the event listeners after doing the innerHTML, as you can see from the code. I simply cannot see the event being added, the function is firing but not adding the event.
Also, I am using a custom $ function, just a return document.getElementById(id) sort of function.
Check to see if your $() uses any caching. If it caches old references to elements then when innerHTML is set the $("id") will return a reference to an invalid reference.
[edit] The references are more likely valid even though they are no longer visible in the HTML DOM. So modifying the detached elements works but it doesn't do any good since they are detached from the DOM.
Sukima psychic ability's catched the main problem: your custom $ function (to replace document.getElementById) used a caching mechanism.
After some testing (out of personal curiosity) it turned out that as long as you have some reference to an element, the element is still accessible, even after elm.parentNode.removeChild or a full elm.parentNode.innerHTML rewrite (at least, in FF).
In other words: the events WERE added, every time, just to the wrong/old/previous elements instead of the new ones. Otherwise there would also have been errors that the elements didn't exist and thus didn't have an addEventListener method..
See this crude test-fiddle for proof: http://jsfiddle.net/G28Lu/
I toyed around with a $ function (as you haven't posted yours yet) and gave it an 'refresh'-flag to go with the optional cache mechanism:
var $=function(/*cache_enabled*/c){ // getElementById with optional caching & refresh
return c ?( c={}, function(s,r){return (!r && c[s]) || (c[s]=document.getElementById(s));}
):( function(s){return document.getElementById(s);} );
}(true); // or nothing or 0 etc to disable the cache
Note: dynamically created functions are slow (for some reason) so it has 2 functions so the browser can run them optimized (and the other should be cleared by the garbage collector).
To request a fresh just add a flag that evaluates to true as second argument, like: $('elm_id', 1)
Then I modified your addMainMenuListeners function a little to first test for the existence of a fresh getElementById and then add the eventListener via the freshly updated cached reference (so, essentially I changed nothing in the flow of your routine).
formatted to emphasize what changed and how it works
function addMainMenuListeners(){
$( 'start' , 1) && $( 'start' ).addEventListener('click', startGame);
$('instructions', 1) && $('instructions').addEventListener('click', instructions);
$( 'settings' , 1) && $( 'settings' ).addEventListener('click', settings);
$( 'back' , 1) && $( 'back' ).addEventListener('click', resetMainMenu);
$( 'volume' , 1) && $( 'volume' ).addEventListener('change', function(){
saveAndChangeVolume($('volume').value);
});
}
Finally, putting above 2 functions and the rest of your functions/html into this fiddle rendered a working result: http://jsfiddle.net/xHUGu/ !
Note: I had to substitute a dummy function startGame otherwise there would be a fatal error. The missing volume-functions were not critical.
I would like to point out that this is not really the way to go with your interface, there would be a lot of work you could save both yourself and the browser. You might want to look into div's (containing your subsections of html) and toggle them so there is only one visible. Like tabs. (Hint for google-query).
Credit still goes to Sukima ('the force is strong in this one'), but I felt it was a good idea to share the correct explanation to your problem (with proof) and not to waste the work that was done anyway (out of curiosity).
Hope this helps!
Disabling caching on the $ function worked. It was referencing to a destroyed HTML element, and that's why it didn't work. Also, setTimeouts helped for reliability in the case that innerHTML didn't execute in time.

Javascript: How to temporarily disable all actions on the page?

On a page with Ajax event, I want to disable all actions until the Ajax call returns (to prevent issues with double-submit etc.)
I tried this by prepending return false; to the current onclick events when "locking" the page, and removing this later on when "unlocking" the page. However, the actions are not active any more after they are "unlocked" -- you just can't trigger them.
Why is this not working? See example page below. Any other idea to achieve my goal?
Example code:
both the link and the button are showing a JS alert; when pressing lock, then unlock the event handler is the same as it was before, but doesn't work...?!?
The code is meant to work with Trinidad in the end, but should work outside as well.
<html><head><title>Test</title>
<script type="text/javascript">
function lockPage()
{
document.body.style.cursor = 'wait';
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
if (typeof TrPage != "undefined")
{
TrPage.getInstance().getRequestQueue().addStateChangeListener(unlockPage);
}
}
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'wait';
if (el[i].onclick)
{
var newEvent = 'return false;' + el[i].onclick;
alert(el[i].onclick + "\n\nlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
function unlockPage(state)
{
if (typeof TrRequestQueue == "undefined" || state == TrRequestQueue.STATE_READY)
{
//alert("unlocking for state: " + state);
document.body.style.cursor = 'auto';
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
}
}
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.cursor = 'auto';
if (el[i].onclick && el[i].onclick.search(/^return false;/)==0)
{
var newEvent = el[i].onclick.substring(13);
alert(el[i].onclick + "\n\nunlock -->\n\n" + newEvent);
el[i].onclick = newEvent;
}
}
}
</script>
<style type="text/css">
</style>
</head>
<body>
<h1>Page lock/unlock test</h1>
<p>Use these actions to lock or unlock active elements on the page:
lock,
unlock.</p>
<p>And now some elements:</p>
<a onclick="alert('This is the action!');return false;" href="#">link action</a>
<input type="button" value="button action" onclick="alert('This is another action!')"/>
</body>
</html>
Thanks guys for your ideas and answers.
Now I see that I have mixed up Strings and functions, which obviously can't work ;(
I should have made clear that we use some Web FW and tag libraries (Trinidad) which create the event handling (and Ajax) code, hence I can't edit that directly or use synchronous Ajax etc.
Moreover, Ajax is only one scenario where this code should be executed. It's purpose is to prevent the user to double-submit a page/action, which is also relevant for non-Ajax pages where you could kind of doulbe-click on a button. I know that this is not really safe, and it's only meant to be a "convenience" thingy to avoid getting the navigation error page too often (we have server-side protection, of course).
So, will try the div overlay, probably.
Thanks again,
Christoph.
How about setting up a global var
actions_disabled = 0
increment when the AJAX call starts then decrement when it finishes. All your "action" handlers can then start with
if (actions_disabled) return false;
Much simpler than debugging self-modifying code!
Alternatively, to lock your controls you could set:
control.disabled="disabled"
which will have the bonus of greying them out, making it obvious to the user that they can't submit. To unlock, simply set:
control.disabled=""
NEW IDEA BASED ON COMMENTS (can't quote code in comments, it appears ...):
You can always just hang extra attributes off Javascript objects:
To lock, you could:
control.onclick_old = control.onclick
control.onclick = "return false;"
To unlock, you could:
control.onclick = control.onclick_old
I once achieved this goal by creating a DIV that covered the area I wanted disabled, setting its z-index higher than any of the other elements on the page, and then setting its opacity to 0. By default, this DIV was hidden by display: none, so that it wouldn't interfere with anything. However, when I wanted the area disabled, I just set its display to block.
Steve
AJAX. Asynchronous. Just make the HTTP request synchronous. Problem solved.
The problem with your code is a result of not coming to grips with types in javascript.
When you say:
var newEvent = 'return false;' + el[i].onclick
what this does is coerce el[i].onclick (which is a function) to a string, then concatenates it to the string 'return false;'. Then when you reassign it as so:
el[i].onclick = newEvent;
onclick which was previously a function is now a string.
Then you attempt to resurrect your old function from the string by taking a substring:
var newEvent = el[i].onclick.substring(13);
which is fine, except newEvent is still a string! So when you assign it back to onclick again, you are assigning the string representation of the original function, not the function itself.
You could use eval to evaluate the string and return the function, but please don't do that. There are a number of better ways to do this, as has been suggested by other commenters.
I would also question why you wish to use AJAX at all if you don't want to allow asynchronous requests.
Put lockPage() at top of activete() function, and unlockPage() at bottom of deactivate().
activate: function() {
function lockPage()
{
lockElements(document.getElementsByTagName("a"));
lockElements(document.getElementsByTagName("input"));
lockElements(document.getElementsByTagName("button"));
};
function lockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="none";
}
};
lockPage();
// ...
},
deactivate: function() {
// ...
function unlockPage() {
unlockElements(document.getElementsByTagName("a"));
unlockElements(document.getElementsByTagName("input"));
unlockElements(document.getElementsByTagName("button"));
};
function unlockElements(el)
{
for (var i=0; i<el.length; i++)
{
el[i].style.pointerEvents="auto";
}
};
unlockPage();
},
Using a div overlay does not prevent a user from tab-ing into your page. Usually that is OK, since most users do not tab through a page anyhow.
If you use any keyboard shortcuts on your page, they will still be available, so separate handling will be needed for those.
Alse, I assume that clicking an element that can have focus (eg. an <a> tag), then pressing enter, would still cause a double submit.

Categories