JavaScript Tab Amount - javascript

is there a way to check if the user has more than one tab open (not globally, only on my website) or how many tabs the user has open?
In the best case i need the amount but a boolean value would be enough.
I need a solution in vanilla JavaScript (ES6) without jQuery or something else.
Thank you in advance.

You could create an ID for every opened tab and save it in an array in the localStorage (document.cookie would also work, but requires a bit more effort with getting and setting):
var tabId = Math.random();
var tabs = JSON.parse(localStorage.getItem('tabs')) || [];
tabs.push(tabId);
localStorage.setItem('tabs', JSON.stringify(tabs));
Now, to check how many tabs we have open on the current domain, we check the length of the tabs array in the localStorage:
function getTabCount()
{
return JSON.parse(localStorage.getItem('tabs')).length;
}
Now, you can use getTabCount() to get the amount of tabs that are currently open.
Finally, we need to make sure our tabId is removed from the array when we close it:
window.addEventListener('beforeunload', function(e)
{
tabs = JSON.parse(localStorage.getItem('tabs'));
var index = tabs.indexOf(tabId);
if (index !== -1)
tabs.splice(index, 1);
localStorage.setItem('tabs', JSON.stringify(tabs));
});
Do keep in mind that you need to keep using getTabCount() to check if any tabs have been opened or closed since you last used it.

Related

Array with localStorage doesn't save or gets overwritten?

In order to summarize the problem I'll explain what the task is first.
So for the eastern event, we are going to add 3 bunny images across a website (different pages, same domain, same website). Once you've found and clicked on all 3 images it should open a new window with a specific URL.
Right now I managed to write the code which saves the clicks of the 3 pictures in an array and then opens the new window with an URL. But sadly it doesn't work once I change the page. The Array either didn't save in the browser storage or gets overwritten once I open a new page.
I'm not exactly sure what the issue is right now. I hope any of you could help me out.
I've tried to work with localStorage and sessionStorage but I don't think I used them properly. I'll provide you with my current code below.
Javascript
$(function(){
var imageStore = [];
$('.osterhasen').click(function(e){
localStorage.id = $(this).attr('id');
// returns index of the element in the array, if the element was not found returns false
var imageExists = $.inArray(localStorage.id, imageStore);
if (imageExists >= 0){
// If element exists, do nothing
e.preventDefault;
} else {
// If element doesn't exist, add element
imageStore.push(localStorage.id);
}
localStorage.setItem('imageStore', JSON.stringify(imageStore));
localStorageimageStorage = JSON.parse(localStorage.getItem('imageStore'));
console.log(localStorageimageStorage);
if (localStorageimageStorage.length == 3) {
window.open('https://www.google.ch');
}
});
});
HTML
<body>
<div class="container">
<div id="1" class="osterhasen"><img src="img/choco.png"></img></div>
<div id="2" class="osterhasen"><img src="img/geschichte.png"></img></div>
<div id="3" class="osterhasen"><img src="img/mitarbeiter.jpg"></img></div>
</div>
</body>
In the end the clicks on the images should be saved in the browser storage across the whole website and once you've found all 3 images it should open a new window with a specfic URL.
Thank you very much for your time.
Best regards
You can't assign properties to localStorage like this (it doesn't exist, and you should be using it's setItem method anyway):
localstorage.id = $(this).attr('id');
var imageExists = $.inArray(localstorage.id, imageStore);
So assign id to a variable instead:
const id = $(this).attr('id');
const imageExists = $.inArray(id, imageStore);
Working version
Yes, you're overriding the key every time. To store an array as you want, you can try the following:
$(function(){
var imageStore = [];
$('.osterhasen').click(function(e){
if(localStorage.getItem('imageStore') === null){ // check if such key exists
localStorage.setItem('imageStore', JSON.stringify([$(this).attr('id')])); // if it doesn't create an array with first item imageStore and set it to key imagestore
} else {
var currentStorage = JSON.parse((localStorage.getItem('imageStore')));
if(!currentStorage.includes($(this).attr('id')){ // if id doesn't exist add it.
currentStorage.push($(this).attr('id')); // push to new image inside of it
localStorage.setItem('imageStore', JSON.stringify(currentStorage)); // set the key to the new value
}
}
localStorageimageStorage = JSON.parse(localStorage.getItem('imageStore')); // you should have all the 3 pictures here in an array
console.log(localStorageimageStorage);
if (localStorageimageStorage.length == 3) {
window.open('https://www.google.ch');
}
});
});

Access Elements in Another Window

is it possible to write a javascript to access the elements (knowing their id) in another open window? I want to refresh the page and read some elements' contents.
You can totally use sessionStorage ! Here is the Documentation
If user direct to next page in same tab, sessionStorage can easily save you data and reuse in next page.
// set in page A
window.sessionStorage.setItem('youdata', 'youdata');
// or window.sessionStorage['youdata'] = 'youdata';
// get in page B
var youdata = window.sessionStorage.getItem('youdata');
// or var youdata = window.sessionStorage['youdata'];
That's it! very simple!
If you'll open a new tab, you can use localStorage. Here Is the Documentation
The usage of localStorage is like the way of sessionStorage.
While do saving information for other pages, these two method only need browsers' support.

How to open different popups with the same title in CasperJS?

I'm trying to automate some tasks using casperJS, and I need to open multiple popups. However, all popups have the exact same url (http://.../printit.aspx/...), so that whenever I use
this.withPopup(/printit/, function() {...});
it always opens the first popup. I can't access the other ones.
I suppose there are two possibilities :
close each popup after visiting it, but I can't find how to do this
accessing popups using another way than the URL regex /printit/. Maybe using casper.popups, but the documentation is very vague about this.
There is no easy and documented way of disambiguating two popups. The documentation says that casper.popups is an array-like property. So you could iterate over it. Judging by the code, the popups property itself is a pagestack. One can easily modify the pagestack.findByRegExp() function to do this kind of thing.
It seems that the casper.popups property contains duplicate entries, so one can filter them out.
casper.findAllPopupsByRegExp = function(regexp){
var popups = this.popups.filter(function(popupPage) {
return regexp.test(popupPage.url);
});
if (!popups) {
throw new CasperError(f("Couldn't find popup with url matching pattern %s", regexp));
}
// remove duplicates
var uniquePopups = [];
popups.forEach(function(p){
if (uniquePopups.indexOf(p) === -1) {
uniquePopups.push(p);
}
});
return uniquePopups;
}
casper.withPopup() accepts three types of inputs to identify a popup page. The third one is a the page object itself. So you can retrieve the matching popup page objects with findAllPopupsByRegExp(), select the one that you want and pass that to withPopup() to change into its context:
casper.then(function(){
var popups = this.findAllPopupsByRegExp(/printit/);
this.withPopup(popups[1], function(){
...
});
});
In my case i have a list of links. Every link calls some javascript that opens a new tab(=popup in casperjs), always with the same url (...\View.aspx).
Inside the tab i have to click a button that changes the url in the tab (...\List.aspx).
on("popup.loaded"...) is called twice, pushing every new page in the casper.popups array. They usually alternate, but for some reason (i guess asyncrony) not always: sometimes casper.popups[/*LAST*/].url matches /View\.aspx/, sometimes it matches /List\.aspx/.
I always had to use casper.withPopup( /*LAST VIEW.ASPX LOADED*/, ...); that was not always the last popup loaded and neither the one matching /View.aspx/ (it could be one of the oldes), so i had to find the latest loaded matching /View\.aspx/.
Here's my solution:
var is_view_loaded=false;
casper.on('popup.loaded', function(page) {
if(page.url.match(/View\.aspx/)) {
is_view_loaded=true;
}
}
// return last popup which url matches the required regexp
casper.getLastRegExPopup=function(regex) {
var l=casper.popups.length;
var i=l-1;
while(!regex.test(casper.popups[i].url)) {
i--;
if(i<0) return null;
}
return casper.popups[i];
}
Then in my core steps:
.
.
// clicked the link, must wait for popup (but which?!)
casper.waitFor(
function test() {
return is_view_loaded;
},
function then() {
var popup=casper.getLastRegExPopup(/View\.aspx/);
casper.withPopup(popup, function() {
// do things with "this"
});
is_view_loaded=false;
}
//, timeout
);
.
.

JavaScript button link on one page to show hidden div on another page

I have a home.html and faq.html.
The faq page has many questions and their answers listed consecutively down the page. I want the answers hidden until a question is clicked that opens that answer. When another question is clicked, it will close the previous answer and open the new one. I'd like to use slideUp/slideDown or fadeIn/fadeOut for that, but not essential.
The home page has a link that when clicked, I want taken to the faq page and also have 'open' a specific answer, both from the one click; slideDown or fadeIn again not essential.
Is there a JavaScript that can be used to perform the task?
I have seen some posts with setvisibility type code somewhat acceptable for the faq page, but not that I can activate from my home page.
I am a novice, so I ask for the html code too that I can insert on both pages. A bigger chore than usual I know!
Many thanks.
NOTE
I now see a problem with my idea because of the following. I didn't mention it before but it would be neccessary.
Let me try to explain it.
I now find that to have a link on one page open another page at a particular position down that page will
correctly position the page to that point IF previous content on that page is not hidden.
If previous content is visibility: hidden;, that point where the page will open at is innacurate. In other words, it opens at a point below intended, a point at which is correct if previous content is not hidden.
I hope that makes some sense.
I will soon look at whether display:none; is better suited for this before I look at my original questions.
Thanks to those who have helped me to date, you have spent a lot of time for me.
Try this one
Reference
$(function() {
$('div.answer').hide();
$('a.question').before('<span class="faqplusminus dark nounderline">[+]</span>');
$('a.question').click(function() {
$('div.answer').slideUp('slow', 'easeInOutExpo');
$('span.faqplusminus').html('[+]');
var slidedownelement = $(this).closest('div.faq').find('div.answer').eq(0);
if(!slidedownelement.is(':visible')) {
slidedownelement.slideDown('slow', 'easeInOutExpo');
slidedownelement.parent().find('span.faqplusminus').html('[-]');
}
});
});
You can add querystring parameters to the request for the faq page and read the querystring using javascript on the faq page. Depending on the parameters you pass, you could do your desired animations.
In home.html: You would create the links with the query string param like so:
Link to FAQ
In faq.html: you would need to read the querystring parameter and depending on the value, call the javascript function that actually handles the show/hide of questions.
To read querystring parameters in javascript you need to parse window.location.search. Here is a javascript helper that you can use:
var QueryString = function () {
// This function is anonymous, is executed immediately and
// the return value is assigned to QueryString!
var query_string = {};
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
// If first entry with this name
if (typeof query_string[pair[0]] === "undefined") {
query_string[pair[0]] = pair[1];
// If second entry with this name
} else if (typeof query_string[pair[0]] === "string") {
var arr = [ query_string[pair[0]], pair[1] ];
query_string[pair[0]] = arr;
// If third or later entry with this name
} else {
query_string[pair[0]].push(pair[1]);
}
}
return query_string;
} ();
You can then access QueryString.question

backbone.js and local storage with multiple browser tabs / windows overwrites data

just a very short question on using Backbone.js with LocalStorage:
I'm storing a list of things (Backbone collection) in LocalStorage. When my website is open in multiple browser windows / tabs and the user in both windows adds something to the list, one window's changes will overwrite the changes made in the other window.
If you want to try for yourself, just use the example Backbone.js Todo app:
Open http://backbonejs.org/examples/todos/index.html in two browser tabs
Add an item 'item1' in the first tab and 'item2' in the second tab
Refresh both tabs: 'item1' will disappear and you'll be left with 'item2' only
Any suggestions how to prevent this from happening, any standard way to deal with this?
Thxx
The issue is well-known concurrency lost updates problem, see Lost update in Concurrency control?.
Just for your understanding I might propose the following quick and dirty fix, file backbone-localstorage.js, Store.prototype.save:
save: function() {
// reread data right before writing
var store = localStorage.getItem(this.name);
var data = (store && JSON.parse(store)) || {};
// we may choose what is overwritten with what here
_.extend(this.data, data);
localStorage.setItem(this.name, JSON.stringify(this.data));
}
For the latest Github version of Backbone localStorage, I think this should look like this:
save: function() {
var store = this.localStorage().getItem(this.name);
var records = (store && store.split(",")) || [];
var all = _.union(records, this.records);
this.localStorage().setItem(this.name, all.join(","));
}
You may want to use sessionStorage instead.
See http://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage.
Yaroslav's comment about checking for changes before persisting new ones is one solution but my suggestion would be different. Remember that localStorage is capable of firing events when it performs actions that change the data it holds. Bind to those events and have each tab listen for those changes and view re-render after it happens.
Then, when I make deletions or additions in one tab and move over to the next, it will get an event and change to reflect what happened in the other tab. There won't be weird discrepancies in what I'm seeing tab to tab.
You will want to give some thought to making sure that I don't lose something I was in the middle of adding (say I start typing a new entry for my to-do list), switch to another tab and delete something, and then come back I want to see the entry disappear but my partially typed new item should still be available for me.

Categories