Clearing local storage in JavaScript - javascript

I am working on a simple todo list with vanilla JavaScript.
I have succeeded in storing the user's task in local storage and displaying the task on the frontend.
There is a clear task button to remove both tasks from local storage and on the frontend.
It works but not perfectly.
It fails when I do the following:
Add a task
Clear a task
Add a new task and that particular task only appears on the front-end, but on local storage the previous cleared tasks and the new task appears.
If I then reload the browser, the previous task that was cleared both in the frontend and local storage appears both on frontend and local storage.
Please how do I make it work perfectly?
i.e once I clear the task in the local storage, the task does not appear again.
Here is my code below
JavaScript Code snippet
let task = document.querySelector('input');
const form = document.querySelector('form');
const ul = document.querySelector('ul');
const clearTask = document.querySelector('#clear');
// A list for task in local storage
const itemsLocal = localStorage.getItem("items") ? JSON.parse(localStorage.getItem("items")) : [];
localStorage.setItem("items", JSON.stringify(itemsLocal))
// convert local storage data to something I can work with in userData variable
const userData = JSON.parse(localStorage.getItem("items"));
// Function to add task
const addTask = (text) => {
// Create li element
const li = document.createElement('li');
// Create text node
li.appendChild(document.createTextNode(text));
ul.appendChild(li);
}
form.addEventListener('submit',(e) => {
e.preventDefault();
// Add user task to local storage
itemsLocal.push(task.value);
localStorage.setItem("items", JSON.stringify(itemsLocal))
addTask(task.value);
// Clear input field
task.value = '';
})
userData.forEach((data)=> {
addTask(data);
});
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
userData.length = 0;
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
})
HTML
<body>
<form action="">
<input type="text">
<button type="submit">Add Task</button>
</form>
<div>
<ul>
</ul>
</div>
<button id="clear">Clear Task</button>
<script src="main.js"></script>
</body>
P.s I am new a bit new to JavaScript.

In your clearTask event listener you have to also clear your itemsLocal array.
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
itemsLocal.length = 0; // clear it here... (you got to add this line)
userData.length = 0;
while (ul.firstChild) {
ul.removeChild(ul.firstChild);
}
});
Currently, since you're not clearing it, you're adding the new value to the array which still contains the old values and are storing it to local storage.
form.addEventListener('submit',(e) => {
e.preventDefault();
// 'itemsLocal' still has your old values here and you're just appending to it
itemsLocal.push(task.value);
// storing old items and the new one in the local storage here
localStorage.setItem("items", JSON.stringify(itemsLocal));
// you're rendering only the currently added item so you get the
// fake feeling that your list is fine and that local storage has issues
addTask(task.value);
// Clear input field
task.value = '';
});

just make the user data array empty in the clearTask like this:
clearTask.addEventListener('click', () =>{
localStorage.removeItem("items");
itemsLocal = [];
userData = [];
})

elementsList.innerHTML = '' will clear all items in the list.
Below code works perfectly with localStorage and it has one more additional function that can remove individual item.
Html:
<section class="container">
<h1>TO DO LIST</h1>
<ul></ul>
<div class="footer">
<input type="text" placeholder="Title..." />
<button class="enter">Enter</button>
</div>
<div>
<button class="clear">
Clear
</button>
</div>
</section>
Script:
const ul = document.querySelector("ul");
const input = document.querySelector("input");
const enterBtn = document.querySelector(".enter");
const clearBtn = document.querySelector(".clear");
const LIST_LS = "lists";
let lists = [];
function saveStorage() {
localStorage.setItem(LIST_LS, JSON.stringify(lists));
}
function clearStorage() {
lists = [];
ul.innerHTML = "";
saveStorage();
}
function loadStorage() {
const loadStorage = localStorage.getItem(LIST_LS);
if (!loadStorage) {
return;
}
const parsedList = JSON.parse(loadStorage);
parsedList.forEach(list => createItem(list.text));
}
function onAdd() {
const text = input.value;
if (!text) {
return input.focus();
}
createItem(text);
input.value = "";
input.focus();
}
function createItem(text) {
const id = lists.length + 1;
const itemRow = document.createElement("li");
itemRow.setAttribute("class", "item__row");
itemRow.innerHTML = `${text} <i class="fas fa-trash-alt" data-id=${
itemRow.id
}></i>`;
itemRow.id = id;
const delBtn = itemRow.querySelector(".fa-trash-alt");
delBtn.addEventListener("click", deleteItem);
ul.appendChild(itemRow);
lists.push({ text, id });
saveStorage();
return itemRow;
}
function deleteItem(event) {
const trashBtn = event.target;
const li = trashBtn.parentNode;
ul.removeChild(li);
const cleanStorage = lists.filter(toDo => toDo.id !== +li.id);
lists = cleanStorage;
saveStorage();
}
function init() {
loadStorage();
enterBtn.addEventListener("click", () => onAdd());
input.addEventListener("keypress", event => {
if (event.key === "Enter") {
onAdd();
}
});
clearBtn.addEventListener("click", () => clearStorage());
}
init();
https://codesandbox.io/s/damp-morning-csiny

Check this out, I think you might learn from the code:
//<![CDATA[
/* js/external.js */
let get, post, doc, htm, bod, nav, M, I, mobile, S, Q, hC, aC, rC, tC, shuffle, rand, Lister; // for use on other loads
addEventListener('load', ()=>{
get = (url, success, context)=>{
const x = new XMLHttpRequest;
const c = context || x;
x.open('GET', url);
x.onload = ()=>{
if(success)success.call(c, JSON.parse(x.responseText));
}
x.send();
}
post = function(url, send, success, context){
const x = new XMLHttpRequest;
const c = context || x;
x.open('POST', url);
x.onload = ()=>{
if(success)success.call(c, JSON.parse(x.responseText));
}
if(typeof send === 'object' && send && !(send instanceof Array)){
if(send instanceof FormData){
x.send(send);
}
else{
const fd = new FormData;
for(let k in send){
fd.append(k, JSON.stringify(send[k]));
}
x.send(fd);
}
}
else{
throw new Error('send argument must be an Object');
}
return x;
}
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
S = (selector, within)=>{
var w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
var w = within || doc;
return w.querySelectorAll(selector);
}
hC = function(node, className){
return node.classList.contains(className);
}
aC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.add(...a);
return aC;
}
rC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.remove(...a);
return rC;
}
tC = function(){
const a = [].slice.call(arguments), n = a.shift();
n.classList.toggle(...a);
return tC;
}
shuffle = array=>{
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
rand = (min, max)=>{
let mn = min, mx = max;
if(mx === undefined){
mx = mn; mn = 0;
}
return mn+Math.floor(Math.random()*(mx-mn+1));
}
Lister = function(inInput, addButton, outList, clearButton, reverseButton, controlDiv = null){
const o = localStorage.listObj ? JSON.parse(localStorage.listObj) : {lastFirst:true, list:[]}, la = o.list;
outList.innerHTML = '';
this.lastFirst = o.lastFirst;
this.save = ()=>{
localStorage.listObj = JSON.stringify(o);
return this;
}
this.createItem = value=>{
let li = M('li'), x = M('input');
x.className = 'warn'; x.type = 'button'; x.value = 'REMOVE'; li.textContent = value; li.appendChild(x);
x.onclick = ()=>{
for(let i=0,c=outList.children,l=c.length; i<l; i++){
if(c[i] === li){
la.splice(i, 1); break;
}
}
outList.removeChild(li);
if(controlDiv && !outList.hasChildNodes())aC(controlDiv, 'hid');
this.save();
}
return li;
}
this.addItem = value=>{
let v = value.trim();
if(v !== ''){
let li = this.createItem(v), fc = outList.firstChild;
if(this.lastFirst && fc){
outList.insertBefore(li, fc); la.unshift(v);
}
else{
outList.appendChild(li); la.push(v);
}
this.save();
if(controlDiv)rC(controlDiv, 'hid');
}
}
const addIt = ()=>{
this.addItem(inInput.value); inInput.value = ''; rC(inInput, 'good');
}
addButton.onclick = ()=>{
addIt();
}
inInput.onkeydown = e=>{
if(e.key === 'Enter')addIt();
}
inInput.oninput = function(){
const f = this.value.trim() === '' ? rC : aC;
f(this, 'good');
}
clearButton.onclick = function(){
localStorage.removeItem('listObj'); la.splice(0); outList.innerHTML = '';
if(controlDiv)aC(controlDiv, 'hid');
}
this.reverse = ()=>{
la.reverse(); outList.innerHTML = '';
la.forEach(v=>{
outList.appendChild(this.createItem(v));
});
o.lastFirst = this.lastFirst = !this.lastFirst; localStorage.listObj = JSON.stringify(o);
}
reverseButton.onclick = ()=>{
this.reverse();
}
if(la.length){
la.forEach(v=>{
outList.appendChild(this.createItem(v));
});
}
if(controlDiv && outList.hasChildNodes())rC(controlDiv, 'hid');
}
// magic under here
const lister = new Lister(I('input_item'), I('add_item'), I('items'), I('clear'), I('reverse'), I('control'));
}); // end load
//]]>
/* css/external.css */
*{
box-sizing:border-box; font:22px Tahoma, Geneva, sans-serif; color:#000; padding:0; margin:0; overflow:hidden;
}
html,body,.main{
width:100%; height:100%;
}
.main{
background:#333; overflow-y:auto;
}
input[type=text].good{
border:1px solid #0c0;
}
.hid{
display:none;
}
#lister{
padding:10px; background:#aaa;
}
input{
height:38px; padding:3px 5px;
}
input[type=text]{
width:calc(100% - 70px); background:#fff; border:1px solid #c00; border-radius:3px;
}
input[type=button]{
color:#fff; font-weight:bold; border:0; border-radius:5px; cursor:pointer;
}
#add_item{
width:70px; background:linear-gradient(#679467,#235023);
}
li{
position:relative; height:32px; background:#ccc; padding:3px 10px; margin:5px;
}
.warn{
background:linear-gradient(#b75757,#502323); padding:3px 15px;
}
li>.warn{
position:absolute; right:5px; height:26px; font-size:14px;
}
#control{
background:#bbb; text-align:center; padding:5px; border:5px solid #ccc;
}
#reverse.warn{
background:linear-gradient(#1b7bbb,#147);
}
.warn+.warn{
margin-left:20px;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
<title>Title Here</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script src='js/external.js'></script>
</head>
<body>
<div class='main'>
<div id='lister'>
<input id='input_item' type='text' maxlength='239' /><input id='add_item' type='button' value='ADD' />
<ul id='items'></ul>
<div class='hid' id='control'><input class='warn' id='clear' type='button' value='CLEAR' /><input class='warn' id='reverse' type='button' value='REVERSE' /></div>
</div>
</div>
</body>
</html>
Of course, localStorage will have to be tested on your Server (maybe localhost), since it's an issue on Stack Overflow. They should add virtual localStorage and sessionStorage to this site.

Related

onmouseover -mouseout images

How could I have two pictures,one for show while I am onmouseover and one for hide while I am onmouseout?
<td id="img" onmouseover="showIt(this.src)" onmouseout="hideIt()" src="image1.jpg " src="default.jpg">my box" </td>
function showIt(imgsrc)
{
document.getElementById('img').src=imgsrc;
}
function hideIt()
{
document.getElementById('img').src="default.png";
}
You can achieve this using just CSS as well. Note, CSS is always faster than JS in terms of DOM manipulation.
Also as commented, you will have to use img tag instead
CSS Approach
.hover-toggle {
content: url('https://reputationtoday.in/wp-content/uploads/2019/11/110-1102775_download-empty-profile-hd-png-download.jpg');
width: 100px;
height: 100px;
border: 1px solid gray;
}
.hover-toggle:hover {
content: url('https://img.favpng.com/0/8/3/user-profile-computer-icons-internet-bot-png-favpng-92SBLLR7CwZpN8Vm6MUsuU4Sd.jpg')
}
<img class='hover-toggle'>
As rightly commented by #StackSlave, hover will have issues on mobile device.
:hover is a problem on mobile. I would use Element.onmouseenter and Element.ontouchstart and Element.onmouseleave and Element.ontouchend instead.
JS Approach
function registerEvents() {
const img = document.getElementById('img');
img.addEventListener('mouseenter', showIt)
img.addEventListener('mouseleave', hideIt)
}
function showIt() {
this.src = 'https://img.favpng.com/0/8/3/user-profile-computer-icons-internet-bot-png-favpng-92SBLLR7CwZpN8Vm6MUsuU4Sd.jpg';
}
function hideIt() {
this.src = 'https://reputationtoday.in/wp-content/uploads/2019/11/110-1102775_download-empty-profile-hd-png-download.jpg';
}
function initialize() {
const img = document.getElementById('img');
// set default state
hideIt.call(img)
// Register events
registerEvents()
}
initialize()
#img {
width: 100px;
height: 100px;
border: 1px solid gray;
}
<img id='img'>
Reference:
Detecting hover or mouseover on smartphone browser
Why are inline event handler attributes a bad idea in modern semantic HTML?
use <img> tag instead of <td>
<img src="ss" onmouseover="add(event)" onmouseout="remove(event)">
Java Script
function add(e){
e.target.setAttribute("src", "https://shiharadil.netlify.app/static/media/me.9688d9df.jpg");
}
function remove(e){
e.target.setAttribute("src", "ss");
}
maybe you want to track the original src like this.
<img id="myimg" src="https://images.freeimages.com/images/small-previews/867/volcanic-mt-ngauruhoe-1378772.jpg" onmouseover="mouseover()" onmouseout="mouseout()" />
function mouseover() {
const elm = event.target;
elm.setAttribute("orig_src", elm.src);
elm.src = "https://images.freeimages.com/images/small-previews/647/snowy-mountain-1378865.jpg";
}
function mouseout() {
const elm = event.target;
elm.src = elm.getAttribute("orig_src");
}
https://jsfiddle.net/71zfw8s4/
I would separate your HTML, CSS, and JavaScript, so everything but the HTML is cached. It also makes it easier to reuse code. Here's an in-depth example, of how I might do what you're asking:
//<![CDATA[
/* js/external.js */
let get, post, doc, htm, bod, nav, M, I, mobile, beacon, S, Q, hC, aC, rC, tC, inArray, shuffle, isNum, isInt, rand; // for reuse on other loads
addEventListener('load', ()=>{
get = (url, func, responseType = 'json', context = null)=>{
const x = new XMLHttpRequest;
const c = context || x;
x.open('GET', url); x.responseType = responseType;
x.onload = ()=>{
if(func)func.call(c, x.response);
}
x.onerror = e=>{
if(func)func.call(c, {xhrErrorEvent:e});
}
x.send();
return x;
}
post = (url, send, func, responseType ='json', context = null)=>{
const x = new XMLHttpRequest;
if(typeof send === 'object' && send && !(send instanceof Array)){
const c = context || x;
x.open('POST', url); x.responseType = responseType;
x.onload = ()=>{
if(func)func.call(c, x.response);
}
x.onerror = e=>{
if(func)func.call(c, {xhrErrorEvent:e});
}
let d;
if(send instanceof FormData){
d = send;
}
else{
let s;
d = new FormData;
for(let k in send){
s = send[k];
if(typeof s === 'object' && s)s = JSON.stringify(s);
d.append(k, s);
}
}
x.send(d);
}
else{
throw new Error('send argument must be an Object');
}
return x;
}
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
beacon = (url, send)=>{
let r = false;
if(typeof send === 'object' && send && !(send instanceof Array)){
let d;
if(send instanceof FormData){
d = send;
}
else{
let s;
d = new FormData;
for(let k in send){
s = send[k];
if(typeof s === 'object' && s)s = JSON.stringify(s);
d.append(k, s);
}
}
r = nav.sendBeacon(url, d);
}
else{
throw new Error('send argument must be an Object');
}
return r;
}
S = (selector, within)=>{
var w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
var w = within || doc;
return w.querySelectorAll(selector);
}
hC = (node, className)=>{
return node.classList.contains(className);
}
aC = function(){
const a = [...arguments];
a.shift().classList.add(...a);
return aC;
}
rC = function(){
const a = [...arguments];
a.shift().classList.remove(...a);
return rC;
}
tC = function(){
const a = [...arguments];
a.shift().classList.toggle(...a);
return tC;
}
inArray = (mixed, array)=>{
if(array.indexOf(mixed) === -1){
return false;
}
return true;
}
shuffle = array=>{
let a = array.slice(), i = a.length, n, h;
while(i){
n = Math.floor(Math.random()*i--); h = a[i]; a[i] = a[n]; a[n] = h;
}
return a;
}
isNum = mixed=>typeof mixed === 'number' && !isNaN(mixed); isInt = mixed=>Number.isInteger(mixed);
rand = (min, max)=>{
let mn = min, mx = max;
if(mx === undefined){
mx = mn; mn = 0;
}
return mn+Math.floor(Math.random()*(mx-mn+1));
}
// magic under here - can put on load on another page except end load
const test = I('test'), one = I('one'), two = I('two');
const presto = ()=>{
tC(one, 'out'); tC(two, 'out');
}
if(mobile){
test.ontouchstart = test.ontouchend = presto;
}
else{
test.onmouseenter = test.onmouseleave = presto;
}
}); // end load
//]]>
/* css/external.css */
*{
box-sizing:border-box; font-size:0; color:#000; padding:0; margin:0; overflow:hidden;
}
html,body,.main{
width:100%; height:100%;
}
.main{
background:#333; overflow-y:auto;
}
#test{
position:relative;
}
#test,#test>img{
width:64px; height:64px;
}
#test>img{
position:absolute; top:0; left:0; transition:opacity 0.5s ease-in-out; cursor:pointer;
}
#test>img.out{
opacity:0;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
<title>Title Here</title>
<link type='text/css' rel='stylesheet' href='css/external.css' />
<script src='js/external.js'></script>
</head>
<body>
<div class='main'>
<div id='test'>
<img id='one' src='https://lh4.googleusercontent.com/-dQCbHQsqFnA/AAAAAAAAAAI/AAAAAAAAAAA/AMZuuclQUPeKb5dc9mdspXEBq7nIrM67RA/s96-c/photo.jpg?sz=64' />
<img class='out' id='two' src='https://i.stack.imgur.com/DvKY9.png?s=64&g=1' />
</div>
</div>
</body>
</html>
Look at // magic under here and you'll see the code you can use will be minimal with the above Library I created. Use freely.

How do I get my for loop to run before it prints?

I have this set up in html to allow a user to input a number, then click submit, and I am hoping to change how many periods will come out in the output box. Currently, just one period will print (I know this is calling to the variable declared in the conditional portion). I assume this means it is collecting the value before the loop runs, but I don't know how to fix that. Please help. (I'm happy to provide the rest of the code if needed but I'm fairly certain I have isolated the problem.
<script type="text/javascript">
function makepyramid() {
var numberin = document.getElementById("numberin");
var pyramid = numberin.value;
var mathablenumber = +numberin.replace;
for (n = 0, spaces ="."; n < mathablenumber; n++)
{
var spaces = spaces +=".";
}
var txtOutput = document.getElementById("txtOutput");
txtOutput.value = "Hi there, " + spaces + "!"
} // end makepyramid
</script>
Basically your problem is in the row var mathablenumber = +numberin.replace;. I dont know what does it do and why do you need it. If your input field is number type it already returns you the number type value and you can use it for iteration directly
I have edited your code a little:
function makepyramid() {
var numberin = document.getElementById("numberin");
var pyramid = numberin.value;
// You can leave as one line as well
//for (var n = 0, spaces = '.'; n < pyramid; n++) spaces +=".";
for (var n = 0, spaces = '.'; n < pyramid; n++) {
spaces +=".";
}
var txtOutput = document.getElementById("txtOutput");
txtOutput.value = "Hi there, " + spaces + "!"
} // end makepyramid
makepyramid();
<input id="numberin" value="5" type="number" />
<input id="txtOutput" type="text"/>
I'm not really sure of your goal here. Thought I'd show some technique:
let doc, htm, bod, nav, M, I, mobile, S, Q, aC, rC, tC, CharPyramid; // for use on other loads
addEventListener('load', ()=>{
doc = document; htm = doc.documentElement; bod = doc.body; nav = navigator; M = tag=>doc.createElement(tag); I = id=>doc.getElementById(id);
mobile = nav.userAgent.match(/Mobi/i) ? true : false;
S = (selector, within)=>{
var w = within || doc;
return w.querySelector(selector);
}
Q = (selector, within)=>{
var w = within || doc;
return w.querySelectorAll(selector);
}
aC = function(){
const a = [...arguments];
a.shift().classList.add(...a);
return aC;
}
rC = function(){
const a = [...arguments];
a.shift().classList.remove(...a);
return rC;
}
tC = function(){
const a = [...arguments];
a.shift().classList.toggle(...a);
return tC;
}
CharPyramid = function(char = '*'){
this.char = char;
this.build = height=>{
let p = M('div');
for(let i=1,r,c=this.char,l=height+1; i<l; i++){
r = M('div'); p.appendChild(r);
for(let n=0,d; n<i; n++){
d = M('div'); d.textContent = c; r.appendChild(d);
}
}
return p;
}
}
// magic happens below
const pyr_chars = I('pyr_chars'), pyr = I('pyr'), cp = new CharPyramid('.'), pyr_er = I('pyr_er');
pyr.appendChild(cp.build(+pyr_chars.value));
pyr_chars.oninput = function(){
let v = this.value;
if(v.match(/^[1-9][0-9]*$/)){
aC(this, 'yes'); pyr_er.textContent = pyr.innerHTML = ''; pyr.appendChild(cp.build(+v));
}
else{
rC(this, 'yes'); pyr.innerHTML = ''; pyr_er.textContent = 'Numbers Only';
}
}
}); //
*{
box-sizing:border-box;
}
btml,body{
background:#ccc;
}
label,input,.er{
font:bold 22px Tahoma, Geneva, sans-serif;
}
label{
color:#fff; text-shadow:-1px 0 #000,0 1px #000,1px 0 #000,0 -1px #000;
}
input{
width:100%; color:#000; padding:3px 5px; border:1px solid #c00; border-radius:3px; margin-top:2px;
}
input.yes{
border-color:#0c0;
}
.er{
color:#a00; font-size:14px; text-align:center; margin-top:3px;
}
.pyr{
margin-top:10px; text-align:center;
}
.pyr>div{
display:inline-block; background:#fff; color:#0b0; text-align:center;
}
.pyr>div>div>div{
display:inline-block;
}
<label for='pyr_chars'>Pyramid Height:</label>
<input class='yes' id='pyr_chars' type='number' value='9' min='1' />
<div class='er' id='pyr_er'></div>
<div class='pyr' id='pyr'></div>
Well, I may not get your point exactly,
anyway if you want to make your code be waiting for the for loop you can simply make it async, try this
async function processArray(array) {
array.forEach(item => {
// define synchronous anonymous function
// IT WILL THROW ERROR!
await func(item);
})
}
Also, you have a mistyping when you are initializing the spaces over and over again inside the loop. that will not get your work done at all.
define the var spaces outside the loop.

Why RTCMultiConnection Event Type is always 'local'?

I'm trying to create a basic video chat using RTCMultiConnection Webrtc.
The code works fine in Safari browser but it always fails to show the joiners videos in a simple ios phonegap app. so basically no one can see others videos.
I added iosrtc plugin to my app as well...
Spent days trying to find the issue and I think I am getting close.
I found out that the event.type is always local for everyone.
So, the event.type is never remote for anyone that uses the app.
But when i test the same code in safari browser, the first joiner event.type is local and the rest of the joiners event.type is remote and thats why it works fine in the browser.
Now that I found the issue (sort of), I need to know why this is happening in the phonegap app and how to eradicate it.
This is my entire code, you can run this code directly in your browser as is and it will work fine:
Javascript:
// ......................................................
// .......................UI Code........................
// ......................................................
document.getElementById('open-room').onclick = function() {
disableInputButtons();
connection.open(document.getElementById('room-id').value, function(isRoomOpened, roomid, error) {
if(isRoomOpened === true) {
showRoomURL(connection.sessionid);
}
else {
disableInputButtons(true);
if(error === 'Room not available') {
alert('Someone already created this room. Please either join or create a separate room.');
return;
}
alert(error);
}
});
};
document.getElementById('join-room').onclick = function() {
disableInputButtons();
connection.join(document.getElementById('room-id').value, function(isJoinedRoom, roomid, error) {
if (error) {
disableInputButtons(true);
if(error === 'Room not available') {
alert('This room does not exist. Please either create it or wait for moderator to enter in the room.');
return;
}
alert(error);
}
});
};
document.getElementById('open-or-join-room').onclick = function() {
disableInputButtons();
connection.openOrJoin(document.getElementById('room-id').value, function(isRoomExist, roomid, error) {
if(error) {
disableInputButtons(true);
alert(error);
}
else if (connection.isInitiator === true) {
// if room doesn't exist, it means that current user will create the room
showRoomURL(roomid);
}
});
};
// ......................................................
// ..................RTCMultiConnection Code.............
// ......................................................
var connection = new RTCMultiConnection();
// by default, socket.io server is assumed to be deployed on your own URL
//connection.socketURL = '/';
// comment-out below line if you do not have your own socket.io server
connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';
connection.socketMessageEvent = 'video-conference-demo';
connection.session = {
audio: true,
video: true
};
connection.sdpConstraints.mandatory = {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
};
connection.videosContainer = document.getElementById('videos-container');
connection.onstream = function(event) {
var video = document.createElement('video');
if(event.type === 'local') {
video.setAttribute('class', 'myvideo');
//$('.yourVideo').attr('src', event.stream);
//$('.yourVideo').attr('id', event.streamid);
//alert('local');
/*video.volume = 0;
try {
video.setAttributeNode(document.createAttribute('muted'));
} catch (e) {
video.setAttribute('muted', true);
}*/
}
if (event.type === 'remote') {
alert('remote');
video.setAttribute('class', 'othersvideo');
}
//video.src = URL.createObjectURL(event.stream);
setTimeout(function() {
var existing = document.getElementById(event.streamid);
if(existing && existing.parentNode) {
existing.parentNode.removeChild(existing);
}
event.mediaElement.removeAttribute('src');
event.mediaElement.removeAttribute('srcObject');
event.mediaElement.muted = true;
event.mediaElement.volume = 0;
try {
video.setAttributeNode(document.createAttribute('autoplay'));
video.setAttributeNode(document.createAttribute('playsinline'));
} catch (e) {
video.setAttribute('autoplay', true);
video.setAttribute('playsinline', true);
}
console.log(JSON.stringify(event));
video.srcObject = event.stream;
var width = parseInt(connection.videosContainer.clientWidth / 3) - 20;
var width = $(document).width();
var height = $(document).height();
var mediaElement = getHTMLMediaElement(video, {
/*title: event.userid,*/
buttons: ['full-screen'],
width: width,
showOnMouseEnter: false
});
connection.videosContainer.appendChild(mediaElement);
mediaElement.media.play();
var isInitiator = connection.isInitiator;
if (isInitiator === true && event.type === 'local') {
// initiator's own stream
alert('you are initiator');
}else{
alert('you are remote');
}
mediaElement.id = event.streamid;
//video.play();
}, 5000);
};
var recordingStatus = document.getElementById('recording-status');
var chkRecordConference = document.getElementById('record-entire-conference');
var btnStopRecording = document.getElementById('btn-stop-recording');
btnStopRecording.onclick = function() {
var recorder = connection.recorder;
if(!recorder) return alert('No recorder found.');
recorder.stopRecording(function() {
var blob = recorder.getBlob();
invokeSaveAsDialog(blob);
connection.recorder = null;
btnStopRecording.style.display = 'none';
recordingStatus.style.display = 'none';
chkRecordConference.parentNode.style.display = 'inline-block';
});
};
connection.onstreamended = function(event) {
var mediaElement = document.getElementById(event.streamid);
if (mediaElement) {
mediaElement.parentNode.removeChild(mediaElement);
}
};
connection.onMediaError = function(e) {
if (e.message === 'Concurrent mic process limit.') {
if (DetectRTC.audioInputDevices.length <= 1) {
alert('Please select external microphone. Check github issue number 483.');
return;
}
var secondaryMic = DetectRTC.audioInputDevices[1].deviceId;
connection.mediaConstraints.audio = {
deviceId: secondaryMic
};
connection.join(connection.sessionid);
}
};
// ..................................
// ALL below scripts are redundant!!!
// ..................................
function disableInputButtons(enable) {
document.getElementById('room-id').onkeyup();
document.getElementById('open-or-join-room').disabled = !enable;
document.getElementById('open-room').disabled = !enable;
document.getElementById('join-room').disabled = !enable;
document.getElementById('room-id').disabled = !enable;
}
// ......................................................
// ......................Handling Room-ID................
// ......................................................
function showRoomURL(roomid) {
var roomHashURL = '#' + roomid;
var roomQueryStringURL = '?roomid=' + roomid;
var html = '<h2>Unique URL for your room:</h2><br>';
html += 'Hash URL: ' + roomHashURL + '';
html += '<br>';
html += 'QueryString URL: ' + roomQueryStringURL + '';
var roomURLsDiv = document.getElementById('room-urls');
roomURLsDiv.innerHTML = html;
roomURLsDiv.style.display = 'block';
}
(function() {
var params = {},
r = /([^&=]+)=?([^&]*)/g;
function d(s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
var match, search = window.location.search;
while (match = r.exec(search.substring(1)))
params[d(match[1])] = d(match[2]);
window.params = params;
})();
var roomid = '';
if (localStorage.getItem(connection.socketMessageEvent)) {
roomid = localStorage.getItem(connection.socketMessageEvent);
} else {
roomid = connection.token();
}
var txtRoomId = document.getElementById('room-id');
txtRoomId.value = roomid;
txtRoomId.onkeyup = txtRoomId.oninput = txtRoomId.onpaste = function() {
localStorage.setItem(connection.socketMessageEvent, document.getElementById('room-id').value);
};
var hashString = location.hash.replace('#', '');
if (hashString.length && hashString.indexOf('comment-') == 0) {
hashString = '';
}
var roomid = params.roomid;
if (!roomid && hashString.length) {
roomid = hashString;
}
if (roomid && roomid.length) {
document.getElementById('room-id').value = roomid;
localStorage.setItem(connection.socketMessageEvent, roomid);
// auto-join-room
(function reCheckRoomPresence() {
connection.checkPresence(roomid, function(isRoomExist) {
if (isRoomExist) {
connection.join(roomid);
return;
}
setTimeout(reCheckRoomPresence, 5000);
});
})();
disableInputButtons();
}
// detect 2G
if(navigator.connection &&
navigator.connection.type === 'cellular' &&
navigator.connection.downlinkMax <= 0.115) {
alert('2G is not supported. Please use a better internet service.');
}
$(document).on('click', '.mybtn', function() {
window.cordova.InAppBrowser.open(" https://vps267717.ovh.net/webrtc", "_blank", "location=no,toolbar=yes");
});
HTML:
<!-- Demo version: 2019.01.09 -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Video Conferencing using RTCMultiConnection</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<script>
setTimeout(function() {
navigator.splashscreen.hide();
}, 4000);
// alert dialog dismissed
function alertDismissed() {
}
</script>
<link rel="stylesheet" href="https://rtcmulticonnection.herokuapp.com/demos/stylesheet.css">
<script src="https://rtcmulticonnection.herokuapp.com/demos/menu.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/dist/RTCMultiConnection.min.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/node_modules/webrtc-adapter/out/adapter.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/socket.io/socket.io.js"></script>
<!-- custom layout for HTML5 audio/video elements -->
<link rel="stylesheet" href="https://rtcmulticonnection.herokuapp.com/dev/getHTMLMediaElement.css">
<script src="https://rtcmulticonnection.herokuapp.com/dev/getHTMLMediaElement.js"></script>
<script src="https://rtcmulticonnection.herokuapp.com/node_modules/recordrtc/RecordRTC.js"></script>
<script src="cordova.js"></script>
<script src="ios-websocket-hack.js"></script>
<style>
/* .myvideo{
width:100px !important;
height:100px !important;
background:#ccc;
position:absolute;
top:0;
left:0;
z-index:10;
}
.othersvideo{
width:100% !important;
height:100% !important;
background:#ccc;
position:absolute;
top:0;
left:0;
z-index:0;
}*/
* {
word-wrap:break-word;
}
video {
object-fit: fill;
width: 30%;
}
button,
input,
select {
font-weight: normal;
padding: 2px 4px;
text-decoration: none;
display: inline-block;
text-shadow: none;
font-size: 16px;
outline: none;
}
.make-center {
text-align: center;
padding: 5px 10px;
}
img, input, textarea {
max-width: 100%
}
#media all and (max-width: 500px) {
.fork-left, .fork-right, .github-stargazers {
display: none;
}
}
</style>
</head>
<body>
<button class="mybtn" >click me now</button>
<section class="make-center">
<div>
<label><input type="checkbox" id="record-entire-conference"> Record Entire Conference In The Browser?</label>
<span id="recording-status" style="display: none;"></span>
<button id="btn-stop-recording" style="display: none;">Stop Recording</button>
<br><br>
<input type="text" id="room-id" value="abcdef" autocorrect=off autocapitalize=off size=20>
<button id="open-room">Open Room</button>
<button id="join-room">Join Room</button>
<button id="open-or-join-room">Auto Open Or Join Room</button>
</div>
<div id="videos-container" style="margin: 20px 0;"></div>
<div id="room-urls" style="text-align: center;display: none;background: #F1EDED;margin: 15px -10px;border: 1px solid rgb(189, 189, 189);border-left: 0;border-right: 0;"></div>
</section>
<script src="https://cdn.webrtc-experiment.com/common.js"></script>
</body>
</html>
Any help would be greately appreciated.
Thanks in advance.

Storing a checkbox value in local storage

Im working on a checklist chrome app for myself and would love your help. Basically, I cant figure out how to save a checkbox status. If I check a box and refresh the page, I would like it to stay checked. But I cant seem to code that. Any ideas how to do that? Thank you!!
Edit: I added the html, it gives me the message that my post is mostly code and that I need to add some text, so here I am just writing some more to fulfil this requirement. There is no need reading this. Thanks for the help and sorry for the late edit
function get_todos() {
var todos = new Array;
var todos_str = localStorage.getItem('todo');
if (todos_str !== null) {
todos = JSON.parse(todos_str);
}
return todos;
}
function add() {
var task = document.getElementById('task').value;
var todos = get_todos();
todos.push(task);
localStorage.setItem('todo', JSON.stringify(todos));
show();
return false;
}
function remove() {
var id = this.getAttribute('id');
var todos = get_todos();
todos.splice(id, 1);
localStorage.setItem('todo', JSON.stringify(todos));
show();
return false;
}
function show() {
var todos = get_todos();
var html = '<ul>';
for(var i=0; i<todos.length; i++) {
html += '<li>' + '<input type="checkbox" id="checkbox">' + todos[i] + '<button class="remove" id="' + i + '">delete</button></li>' ;
};
html += '</ul>';
document.getElementById('todos').innerHTML = html;
var buttons = document.getElementsByClassName('remove');
for (var i=0; i < buttons.length; i++) {
buttons[i].addEventListener('click', remove);
};
}
document.getElementById('add').addEventListener('click', add);
show();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
</head>
<body>
<style>
html,body,h1,h3,h4,h6 {font-family: "Roboto";font-size: 24px; sans-serif}
h2 {font-family: "Roboto";font-size: 36px; sans-serif}
h5 {font-family: "Roboto";font-size: 28px; sans-serif}
</style>
<input id="task"><button id="add">Add</button>
<hr>
<div id="todos"></div>
<script src="todo.js"></script>
</body>
</html>
Seeing as no one else has provided an answer, I thought I'd throw in my potential solution, I went with a functional style because why not, it's simple, easy to read, etc...
PS
I included the fallback variable because on Stack Overflow, you can't access local storage when running a snippet.
let fallback = [];
const $e = query => document.querySelector(query);
// Return a todo list.
const getToDoList = () => {
let data = null;
try {
data = JSON.parse(localStorage.getItem('todo'));
} catch (e) {
data = fallback;
}
return data == null || Array.isArray(data) == false ? [] : data;
};
// Set the todo list.
const setToDoList = (data) => {
try {
localStorage.setItem('todo', JSON.stringify(data));
} catch (e) {
fallback = data;
}
};
// Add a task to the todo list.
const addToDo = () => {
const array = getToDoList();
array.push({value: $e("#task").value, checked: false});
setToDoList(array);
};
// Remove a task from the todo list.
const removeToDo = index => {
const array = getToDoList();
array.splice(index, 1);
setToDoList(array);
};
// Allow for the ability to remove an item from the todo list & other stuff..
const dispatchListEvents = () => {
document.querySelectorAll('#app ul li span').forEach(span => {
span.onclick = () => {
removeToDo(span.parentElement.getAttribute('data-index'));
render();
}
});
document.querySelectorAll('#app ul li input').forEach(input => {
input.onclick = () => {
const array = getToDoList();
const object = array[input.parentElement.getAttribute('data-index')];
object.checked = ! object.checked;
setToDoList(array);
render();
}
});
};
// Render the todo list.
const render = () => {
let index = 0;
const template = item => `<li data-index="${index++}">` +
`<input type="checkbox" ${item.checked ? 'checked' : ''} />` +
`${item.value} <span>X</span></li>`;
const re = new RegExp('</li>,', 'g'), replacement = '</li>';
const html = `<ul>${getToDoList().map(i => template(i))}</ul>`;
$e("#app").innerHTML = `${html.replace(re, replacement)}`;
dispatchListEvents();
};
// Allow the user to add a task to the todo list.
const addToListClickHandler = () => {
let result = $e("#task").value.replace(/\ /g, '').length > 0 ? addToDo() : null;
$e("#task").value = null; // Always null as addToDo returns null.
render();
};
// The function that will be fired when the DOM is ready.
const ready = () => {
render(); // Initial render.
$e("#addToList").addEventListener('click', addToListClickHandler);
};
// An insanely lazy implementation of $(document).ready.
const delay = 250;
const fakeOnReady = setTimeout(ready, delay);
body {
font-family: Arial, Helvetica, sans-serif;
}
#app ul li {
max-width: 150px;
}
#app ul li span {
float: right;
color: red;
}
#app ul li span:hover {
cursor: pointer;
}
<h1>To Do List</h1>
<section>
<h2>Add Task</h2>
<input id="task" placeholder="Task..."/>
<input type="button" id="addToList" value="Add"/>
</section>
<hr/>
<section>
<h2>Tasks</h2>
<div id="app">
<!-- Area for the DHTML. -->
</div>
</section>

Is SetInterval() to milliseconds too much for browser?

I have made a JavaScript Stopwatch that works by incementing the value of a variable every second (using setInterval(stopwatchValue, 1000)). I have some issues though: if the user hits pause at, say, 3.8 seconds, the actual pause value will be 3 seconds (when they click resume, the stop watch will continue from 3.0 seconds instead of 3.8).
I DO have a solution to fix this. My idea is too update the stopwatch every split second by doing something like setInterval(stopwatchValue, 1).
My question is: Is it too much for the browser to handle this code every split second:
function stopwatchValue("result") {
document.getElementById("result").innerHTML = timer;
timer++;
}
ALSO: Some people have said you should avoid accessing the DOM more than once if you can. In my case, would it work if I replaced my above code with:
var displayResultHere = document.getElementById("result");
function stopwatchValue("result") {
displayResultHere = timer;
timer++;
}
My Stopwatch: hntr.atwebpages.com/stopwatch/stopwatch.html
What you need is a variable delay.
SetInterval's lack of precision is still fine to use to update what the user sees.
What's not cool is that the rounding effect means I can sit down and rapidly click start and stop over and over for hours and never see this thing tick off a second.
What you need to do is timestamp the clicks, do the math to find the real duration (of time spent while started), calculate what should be display'd, and set a one time (per start click) variable delay before SetInterval's one second ticking starts again.
Do that and no amount of clicking will slow your clock down.
Here is an example of a StopWatch. I just made it. If you know how to read code, which you sort of have to to be on this site, it should be easy to understand.
//<![CDATA[
/* external.js */
var doc, bod, htm, M, I, S, Q, StopWatch, old = onload; // for use on other loads
onload = function(){ // load wrapper - not indented to save space
if(old)old(); // change old var name above and here if using technique on other pages
doc = document; bod = doc.body; htm = doc.documentElement;
M = function(tag){
return doc.createElement(tag);
}
I = function(id){
return doc.getElementById(id);
}
S = function(selector, within){
var w = within || doc;
return w.querySelector(selector);
}
Q = function(selector, within){
var w = within || doc;
return w.querySelectorAll(selector);
}
StopWatch = function(outputDiv, decimals, interval){
var dm = typeof decimals === 'number' ? decimals : 3;
var iv = typeof interval === 'number' ? interval : 10;
this.zero = '00:00:0'+(0).toFixed(dm); outputDiv.innerHTML = this.zero;
var st, si, et;
this.start = function(){
if(st === undefined){
st = Date.now();
}
else if(et !== undefined){
st = Date.now()-et+st; et = undefined;
}
si = setInterval(function(){
var t = Date.now()-st, h = Math.floor(t/3600000), hf = h*3600000, m = Math.floor((t-hf)/60000), mf = m*60000, s = Math.floor((t-hf-mf)/1000), sf = s*1000;
s = (s+(t-hf-mf-sf)/1000).toFixed(dm);
if(h.toString().match(/^\d$/))h = '0'+h;
if(m.toString().match(/^\d$/))m = '0'+m;
if(s.toString().match(/^\d(\.?\d*)?$/))s = '0'+s;
var out = h+':'+m+':'+s;
outputDiv.innerHTML = out;
}, iv);
return this;
}
this.stop = function(){
if(si){
if(et === undefined)et = Date.now();
clearInterval(si); si = undefined;
}
return this;
}
this.clear = function(){
this.stop(); st = et = undefined; outputDiv.innerHTML = this.zero;
return this;
}
}
I('f').onsubmit = function(){
return false;
}
var act = I('act'), clear = I('clear'), out = I('out');
var stopWatch = new StopWatch(out);
act.onclick = function(){
if(this.value === 'start'){
stopWatch.start(); this.value = 'stop';
}
else{
stopWatch.stop(); this.value = 'start';
}
}
clear.onclick = function(){
stopWatch.clear(); out.innerHTML = stopWatch.zero; act.value = 'start';
}
} // end load
//]]>
/* external.css */
html,body{
padding:0; margin:0;
}
body{
background:#000; overflow-y:scroll;
}
.main{
width:940px; background:#ccc; padding:20px; margin:0 auto;
}
#out{
display:inline-block; width:85px; background:#fff; padding:5px 10px; cursor:default;
}
#act,#clear{
width:55px; cursor:pointer;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='content-type' content='text/html;charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>StopWatch</title>
<link type='text/css' rel='stylesheet' href='external.css' />
<script type='text/javascript' src='external.js'></script>
</head>
<body>
<div class='main'>
<form id='f' name='f'>
<div id='out'></div>
<input id='act' type='button' value='start' />
<input id='clear' type='button' value='reset' />
</form>
</div>
</body>
</html>
Instead of using setTimeout, use requestAnimationFrame().
When the timer starts, create a new Date, and create a new date every animation frame: subtract the two to get the current value of the timer.
Cache your DOM element into a variable, as you have already done. Something like this:
var result_display = document.getElementById("result");
var timer_is_running = false;
var remembered_time = null;
var timer_start_time;
function start_timer() {
timer_is_running = true;
timer_start_time = new Date();
update_timer_value(accumulated_time);
}
function pause_timer() {
timer_is_running = false;
}
function update_timer_value(acc_time) {
var elapsed_time = new Date() - timer_start_time;
if (remembered_time) {
elapsed_time += remembered_time;
remembered_time = null;
}
var second_value = Math.floor(elapsed_time);
result_display.innerHTML = second_value;
if (timer_is_running) {
requestAnimationFrame(update_timer_value);
}
else {
remembered_time = elapsed_time;
}
}

Categories