Run window.addEventListener('load' ...) only once - javascript

I am wondering if there is any way to run window.addEventListener('load' ...) only the first time the specific page is loaded.
I tried just setting a flag called loaded to false, and only run the code inside the eventListener if loaded === false. Then once it's run, I set loaded to true. But does not work, still runs every time.
Can I perhaprs remove the eventListener once its run?

Keep a localStorage item that contains an array corresponding to all pages that have been loaded so far. Only attach the listener if that page isn't stored in localStorage yet. For example:
const { href } = window.location;
const alreadyLoaded = JSON.parse(localStorage.loaded || '[]');
if (!alreadyLoaded.includes(href)) {
alreadyLoaded.push(href);
localStorage.loaded = JSON.stringify(alreadyLoaded);
window.addEventListener('load', () => {
// rest of your code
});
}

Set the once property to true and it will run only once (doesn't work with Internet explorer)
More information here
const once = {
once : true
};
window.addEventListener('load',callback, once);

Easy way: you can use web storage that is if it's supported. Something like:
if (!localStorage.getItem("listenerLoaded")) {
window.addEventListener('load'...)
localStorage.setItem("listenerLoaded", true);
}
A bit tedious work would be using:
2. cookie(still browser needs support etc).
3. ajax and hold session

No it is not possible a a new execution context will be created every time that page loads.
You can try something like localStorage to save the state.
LocalStorage API helps you to save data which can be accessed later.it is an Object which can be used to access the current origin's local storage space.
For more info visit:
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

Simply set a value in local storage once listener gets loaded then read that value before adding it again.
if (!localStorage.getItem("isLoaded")) {
window.addEventListener('load' ...)
localStorage.setItem("isLoaded", true);
}

Using removeEventListener is a good option:
var callback = function(){
...
}
window.removeEventListener('load',callback);
window.addEventListener('load',callback);

Related

Code in $(document).ready, sometimes works sometimes doesnt

I am trying to dynamically map data from one object to another in an $(document).ready() function, the problem is that it works whenever it wants, the console.logs throughout the code are sometimes working, sometimes not.
var newBtnsObj = {};
$(document).ready(() => {
robots.forEach((robot) => {
newBtnsObj[robot.Name] = {};
newBtnsObj[robot.Name].Buttons = {};
console.log(newBtnsObj);
Object.keys(robot.ConfigData).forEach((dataType) => {
if (dataType === DataTypes.IO) {
Object.values(robot.ConfigData[`${dataType}`]).forEach((IOData) => {
if (IOData.length > 0) {
IOData.forEach((s) => {
console.log("HI");
newBtnsObj[robot.Name].Buttons[s.HtmlID] = {
DataType: dataType,
ID: s.ID,
Val: null,
};
});
}
});
} else {
Object.values(robot.ConfigData[`${dataType}`]).forEach((data) => {
console.log("doublehi");
newBtnsObj[robot.Name].Buttons[data.HtmlID] = {
DataType: dataType,
ID: data.ID,
Val: null,
};
});
}
});
});
});
robots is an array that is generated inside a socket.io event whenever someone connects to the page, I am using NodeJS on the backend to emit the "config" data that is used in the creation of the object and it is the first thing that is emitted when someone opens the page. I am opening the page locally and hosting the page and the server locally.
socket.on("config", (config) => {
config.forEach((robot) => {
robots.push(new Robot(robot.Name, robot.configData, robot.Events));
});
The order in which I load the files in the HTML is first the socket file and then the file with the $(document).ready code.
The problem is that newBtnsObj is sometimes created, sometimes not, if I add any new code and refresh the page it works only on the first refresh and then it is again an empty object. But even that doesnt work everytime, I can't even reproduce it 100%.
Now just for completeness I have added the full code, but even if I remove the newBtnsObj creation and leave it only with the console.log("hi")'s I have the same issue, so the issue is not in the object creation itself (if I copy and paste the code in the browser console it works perfectly). So even if its left to this it still has the same issue:
$(document).ready(() => {
robots.forEach((robot) => {
Object.keys(robot.ConfigData).forEach((dataType) => {
if (dataType === DataTypes.IO) {
Object.values(robot.ConfigData[`${dataType}`]).forEach((IOData) => {
if (IOData.length > 0) {
IOData.forEach((s) => {
console.log("HI");
});
}
});
} else {
Object.values(robot.ConfigData[`${dataType}`]).forEach((data) => {
console.log("doublehi");
});
}
});
});
});
Additionally, I thought that the problem may be that, I am trying to use the robots array before it is created from the config received in the initial socket event, so I have tried placing the code within the event itself, so that it executes right after the event is received, and it worked. Obviously that is the issue, how can I solve it? Is the only way to keep the code in the socket event, and if I want to extract it, I have to make another internal event to let the code know that the robots array is ready?
It seems like you programmed yourself a race-condition there. So your jQuery is executed whenever the page is loaded, regardless if the robots object is there or not. But the generation of the object is related to connections and network timing too. So your script only runs fine, if the generation is faster as your page loading.
And now you might ask why it would not work, if you copy the code below the generation itself. That is a race-condition too. Code inside a jQuery ready state will only be executed after page load. If you paste the code inside another script, like the object generation, the ready state may be registered at a time, where the page was already loaded. So it will never be executed, because you registered it to late.
So what you need to do, is to ensure that the code is only executed after the generation is finished. Maybe by using a callback, an event or something else. But for now, you have two related scripts what run whenever they want and nobody waits for each other. ;)
By the way, just a hint: Using $(document).ready(() => {}) is not best practice and out-dated too. There is a shorthand for that. And you should use jQuery instead of $ for calling it, so your scripts will work in noConflict mode too. So insted use jQuery(($) => {}).

Add Storage Event Listener didn't work

I use method addEventListener with storage parameter to detect changing of storage. However, it didn't work. I do it on Chrome. What is wrong?
window.addEventListener('storage', function(storageEvent){
console.log(storageEvent);
});
function setData(){
localStorage.setItem('superpower', 'heatvision');
}
function clearData(){
localStorage.clear()
}
<button onClick="setData()">Set</button>
<button onClick="clearData()">Clear</button>
It's impossible to detect storage changes in the same window like that. Since you're using getItem and setItem, just try using a wrapper method, and calling that wrapper method instead:
const myLSInterface = {
listeners: [],
set: function(key, value) {
localStorage.key = value;
const thisEventObj = { key, value };
this.listeners.forEach(listener => listener(thisEventObj));
},
addListener: function(listener) {
this.listeners.push(listener);
},
};
myLSInterface.addListener((eventObj) => {
console.log(JSON.stringify(eventObj));
});
myLSInterface.set('foo', 'bar');
See live (can't be done on SO due to security issues):
https://jsfiddle.net/0pgyxotn/
From Web_Storage_API
if you load this page in another tab, then make changes to your
choices in the landing page, you'll see the updated storage
information outputted as the StorageEvent is fired
So basically it means that if the storage is changed from another tab then it will reflect in the landing page.
Take an example of this demo. If you open this link in two different tab , you will see the changes in the second tab is reflecting in the first tab, while he change in the first tab is not reflecting in the same page.
There is one addon on mdn. You can also explore this

Injection of scripts dynamically at button press

A button on my web page upon clicking performs the following action i.e. Injects the script into the page
function InjectToolbar() {
var script = document.createElement('script');
scriptFarfalla.src = 'some_Path'
document.getElementsByTagName('head')[0].appendChild(script);
}
.
.
.
.
.
.
It successfully performs the action desired. But when I reload the page the script is lost
Is there any method/technique with which I can buffer the button's click
Like a toggle button
Toggle.....> script injected
Toggle.....> script detached
Everything that happens in javascript is reset when you leave a page (and return to it). So you need a way to store whether something is loaded or not. This depends on how long you want this to be "saved"/"remembered". There are a few options for you to save this information - Cookies, HTML5 localStorage, HTML5 sessionStorage, and any server session usage you have available (if applicable). So if you want to implement something like this, you now need code onload of your page that checks the specific storage to see if you have set it. If so, inject the script. Here's what I mean:
window.onload = function () {
if (checkIfInjected()) {
scriptInjection(true);
}
}
function toggleInjection() {
if (checkIfInjected()) {
scriptInjection(false);
} else {
scriptInjection(true);
}
}
function scriptInjection(inject) {
if (inject == true) {
var script = document.createElement('script');
script.src = 'some_Path';
script.id = 'injected_script_id';
document.getElementsByTagName('head')[0].appendChild(script);
// Set the storage to say that script is injected
} else {
var the_script = document.getElementById("injected_script_id");
the_script.parentNode.removeChild(the_script);
the_script = null;
// Set the storage to say that script has been removed (or remove from storage altogether)
}
}
function checkIfInjected() {
// The following syntax is wrong for anything - you need to use the correct getter for the storage type you use
return storage.contains("script_injected");
}
<input type="button" id="button1" onclick="toggleInjection();" />
Now it is up to you to determine what storage type you want because they all do different things, including how things are stored, what they are stored for, and how long they are stored for.
You can use a cookie to store the scripts you've injected, and then re-inject them on page load. Cookies and the newer local storage are the usual ways of storing state on the client.

How to remove and clear all localStorage data [duplicate]

This question already has answers here:
Clearing localStorage in javascript?
(14 answers)
Closed 8 years ago.
I need to clear all data i set into localStorage. By this, I mean completely reset localStorage to null when users remove their accounts.
How can i do that with a simple function?
I tried this:
function clearLocalStorage(){
return localStorage= null;
}
But it doesn't work as expected.
localStorage.clear();
should work.
If you want to remove/clean all the values from local storage than use
localStorage.clear();
And if you want to remove the specific item from local storage than use the following code
localStorage.removeItem(key);
It only worked for me in Firefox when accessing it from the window object.
Example...
window.onload = function()
{
window.localStorage.clear();
}
Using .one ensures this is done only once and not repeatedly.
$(window).one("focus", function() {
localStorage.clear();
});
It is okay to put several document.ready event listeners (if you need other events to execute multiple times) as long as you do not overdo it, for the sake of readability.
.one is especially useful when you want local storage to be cleared only once the first time a web page is opened or when a mobile application is installed the first time.
// Fired once when document is ready
$(document).one('ready', function () {
localStorage.clear();
});
Something like this should do:
function cleanLocalStorage() {
for(key in localStorage) {
delete localStorage[key];
}
}
Be careful about using this, though, as the user may have other data stored in localStorage and would probably be pretty ticked if you deleted that. I'd recommend either a) not storing the user's data in localStorage or b) storing the user's account stuff in a single variable, and then clearing that instead of deleting all the keys in localStorage.
Edit: As Lyn pointed out, you'll be good with localStorage.clear(). My previous points still stand, however.

FF extension: global variable that persists over multiple instances of the browser untill reset?

Can we use a Global variable that persists over multiple instances of the browser (FF)?
I am building a ff extension which has to take the host& port name from the user once and then execute the menu options accordingly using that very host and port. This Host and port must remain same untill the user resets it (for which an option will be given)
On declaring the variable as global in the JS file, it would become null everytime the browser is restarted. Can anyone help me out with how and where to save this variable to get the desired functionality. Heres the code to set the preferences. but doesnt work for me
function setInstance() {
if (pref_manager.prefHasUserValue("myvar")) {
getString = pref_manager.getString("myvar");
instance = getString;
}
if (instance == null) {
instance = prompt("Please enter webcenter host and port");
// Setting the values
pref_manager.setString("myvar", instance);
pref_manager.setIntPref("myintvar", 1);
}
}
This function is called as soon as the extension menu option is opened. instance is a global variable which i need to be inputed by the user only once till reset
This is a very old post but still. There are two bits in the given code that don't seem to work.
This code snippet won't work for me:
getString = pref_manager.getString("myvar");
Instead I used the one below and that works for me:
getString = pref_manager.getCharPref("myvar");
Same applies to setter. This won't work for me:
pref_manager.setString("myvar", "mystring");
But this does:
pref_manager.setCharPref("myvar", "mystring");
See: http://www.rietta.com/firefox/Tutorial/prefs.html
You can store it in Firefox's preferences (so it gets stored in about: config and will be available every time Firefox is loaded).
var pref_manager =
Components.classes["#mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
// Setting the values
pref_manager.setCharPref("myvar", "mystring");
pref_manager.setIntPref("myintvar", 1);
// Getting the values
var getString = ""; // Default
if (pref_manager.prefHasUserValue("myvar"))
{
getString = pref_manager.getCharPref("myvar");
}
var getInt = 0; // Default
if (pref_manager.prefHasUserValue("myintvar"))
{
getInt = pref_manager.getIntPref("myintvar");
}
You can find more information on the Mozilla Developer Center page for Preferences, and Adding Preferences To An Extension Page.

Categories