Local Storage may be conflicting with Toggle Slow - javascript

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

Related

Set/Remove localStorage item and EventListener

I have been messing with localStorage and EventListener as seen below (from w3schools but with my messing around). The below always prints "A change was made in the storage area 3" to the div. I'm guessing this is the asynchronous nature of JS and I'd have to do something like promise chaining?
Secondly, and similarly, when I add removing the variable from localStorage and therefore, in EventListener, detecting that the variable is null, the below will not print anything to the div. i'm presuming it's seeing the removal before it can execute the first event (setting the variable) where it actually prints. Anyway, is there a way to pull off setting and removing a variable from localStorage or is this all that asynch stuff and needs promises (formerly callbacks and setTimeouts)? Thank you very much.
<button onclick="changeValue()">Change a Storage Item</button>
<p id="demo"></p>
<script>
window.addEventListener("storage", myFunction);
var x;
var c=0;
var mtv;
function myFunction(event) {
if (event.key==="mytime") {
mtv=localStorage.getItem("mytime");
if (mtv===null) {
} else {
document.getElementById("demo").innerHTML = "A change was made in the storage area "+ c;
}
}
}
function changeValue() {
x = window.open("", "myWindow", "width=200,height=100");
c=c+1
x.localStorage.setItem("mytime", "true");
c=c+1;
x.localStorage.removeItem("mytime");
c=c+1;
x.close();
}
</script>
</body>
</html>
UPDATE...I don't know how to format code in a response but instead of doing the above instant removal of the localStorage object, I went with an add/remove toggle on the localStorage variable...the adding or removal should trigger the ActionListener event in the grandparent window....
if(localStorage.getItem('mytime')){
x.localStorage.removeItem("mytime");
}else{
x.localStorage.setItem("mytime", "true");
}
Ok, the easier that you can do to see what happen is create an HTML with your code and open in your browser. Then, put breakpoints in the event and in the places in which you update the storage. Let's see:
Click on button
Open a window (useless because that window do nothing)
Update the storage (no event fire yet)
Remove the storage (no event fire yet)
Close the window
Now, you receive the 2 events with your c=3 in both cases
If you need track your c values, you can save in the storage that values:
x.localStorage.setItem("mytime", c);
Then, you receive a first event with event.newValue="1" and a second one with event.newValue=null and event.oldValue="1".

jQuery - Load content from another page - only once

I have this small script, which, when clicked on, will show the content from a page where I do a PHP query. My problem is, that if the user click multiply times, it will just load the content from the PHP page multiply times as well...
This is my jQuery code:
$('.notifications-load').on('click',function() {
$('#notifications-holder').load("/?i=notifications");
});
And my HTML:
<i class="fa fa-bell notifications-load"><span class="notification">5</span></i>
<div id="notifications-holder"></div>
This is the PHP page (?i=notifications):
$n=$dbh->prepare("SELECT * FROM users_notifications WHERE userid=0 OR userid=:userid");
$n->bindParam(":userid",$userdata['id']);
$n->execute();
$data=$n->fetchAll();
foreach ($data as $value) {
echo $value['text'];
}
If a user clicks example 3 times on .notifications-load , then the content from /?i=notifications will load 3 times into the #notifications-holder - how can I prevent this?
Believe it or not, one character of difference will fix it:
$('.notifications-load').one('click',function() {
// Here -------------------^
$('#notifications-holder').load("/?i=notifications");
});
jQuery's one function hooks up a handler that it automatically unhooks the first time it's called.
If you have an aversion to using one (as some do, it's really easy to misread it as on), you have a couple of options:
You can create an alias for it:
$.fn.onOnce = $.fn.one;
You can unhook the handler explicitly:
$('.notifications-load').on('click.load',function() {
$('#notifications-holder').load("/?i=notifications");
$('.notifications-load').off('click.load');
});
Besides using $.one you can check to see if $('#notifications-holder') already has a value. Something like:
$('.notifications-load').on('click',function() {
var notification = $('#notifications-holder');
if (!notification.html().trim().length) {
notification.load("/?i=notifications");
}
});

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.

Change element from main page

On my https://getsatisfaction.com/gsfnmichelle/products page, there's a bread crumb trail that says "Products." I'm trying to change that to "Categories." I can get it to do that within the inspector, but when I put the code in the customization script (which is only the main page /gsfnmichelle on the platform I'm using), it doesn't work.
Even though I was able to get the correct element (jQuery('.crumb_link span');) and change it using ( jQuery('.crumb_link span').text('Categories');), I can't figure out how to change it when it's within another page (/products) besides the main one (/gsfnmichelle), since it's the only place where we can insert customization code.
I thought something like this would check for the page and change that element, but it doesn't work:
jQuery(document).ready(function () {
if (window.location.pathname =="/gsfnmichelle/products") {
jQuery('.crumb_link span').text('Categories');}
};
Then I tried to use an if statement to check for the right element, but that doesn't work either:
jQuery(document).ready(function () {
if (jQuery('.crumb_link span').text =='Products') {
jQuery('.crumb_link span').text('Categories');}
};
What am I missing?
You probably want to use either if (jQuery('.crumb_link span').text() == "Products") or jQuery('.crumb_link span:contains(Products)').text('Categories').
Your current condition will never be true.

Toggle Embed Loop

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).

Categories