I try to use a modified socket.io adaption for iobroker (smart home broker) with a setInterval function for showing every second the time ago of the last received timestamp of
It works so far, till the second updated timestamp comes... It seems clearInterval did not stop the interval and the timer is running twice with the old and new timestamp.
How and where do i have to clearInterval correctly?
Updated:
If onUpdate activates the servConn.getStates([stromvIDts]function and received a new timestamp of stromvIDts, the interval should run and show the time ago (every second updated). If (after around 2-3 min) a new value is there, the interval should stop and a new one starting and showing every second the past time of the new timestamp, e.g. "some seconds ago"... 2 Minutes ago ...)
My code:
servConn.namespace = 'mobile.0';
servConn._useStorage = false;
var stromv = 'hm-rpc.1.MEQ123456.1.POWER';
var subscriptions = [stromv];
var states = [];
servConn.init({
name: 'mobile.0',
connLink: 'http://IP:8082',
socketSession: ''
}, {
onConnChange: function(isConnected) {
if (isConnected) {
console.log('connected');
} else {
console.log('disconnected');
}
},
onUpdate: function(id, state) {
setTimeout(function() {
states[id] = state;
let stromvID = states[stromv].val;
let stromvIDts = states[stromv].ts;
//Get States of subsribed states if changed
servConn.getStates([stromvID], (error, states) => {
document.getElementById("stromvAktuell").innerHTML = stromvID + " W/h";
});
servConn.getStates([stromvIDts], (error, states) => {
function stopInterval() {
clearInterval(timerId);
};
timerId = setInterval(function() {
updateTimeAgo();
}, 1000);
function updateTimeAgo() {
let duration = moment(stromvIDts).fromNow();
console.log(duration);
document.getElementById("stromvTs").innerHTML = duration;
};
});
}, 0);
},
onError: function(err) {
window.alert(_('Cannot execute %s for %s, because of insufficient permissions', err.command, err.arg), _('Insufficient permissions'), 'alert', 600);
}
}, false, false);
servConn._socket.emit('subscribe', subscriptions);
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>TEST</title>
<script src="http://IP:8082/socket.io/socket.io.js"></script>
<script src="../tmp/conn.js"></script>
<script src="moment-with-locales.js"></script>
<script>
moment.locale('de')
</script>
</head>
<body>
<div>
Value: <b id="stromvAktuell">unknown</b>
<br> Last update: <b id="stromvTs">unknown</b>
<br>
</div>
</body>
Instead of creating new intervals and clearing the old one, just start one interval and update the value every time onUpdate runs:
If you want the time since the last update, you can use Date.now().
servConn.namespace = 'mobile.0';
servConn._useStorage = false;
var stromv = 'hm-rpc.1.MEQ123456.1.POWER';
var subscriptions = [stromv];
var states = [];
let lastTimestamp = -1;
let duration = -1;
timerId = setInterval(function() {
updateTimeAgo();
}, 1000);
function updateTimeAgo() {
console.log(Date.now() - lastTimeStamp);
document.getElementById("stromvTs").innerHTML = Integer((Date.now() - lastTimeStamp)/1000);
};
servConn.init({
name: 'mobile.0',
connLink: 'http://IP:8082',
socketSession: ''
}, {
onConnChange: function(isConnected) {
if (isConnected) {
console.log('connected');
lastTimestamp = Date.now();
} else {
console.log('disconnected');
}
},
onUpdate: function(id, state) {
setTimeout(function() {
states[id] = state;
let stromvID = states[stromv].val;
let stromvIDts = states[stromv].ts;
//Get States of subsribed states if changed
servConn.getStates([stromvID], (error, states) => {
document.getElementById("stromvAktuell").innerHTML = stromvID + " W/h";
});
lastTimeStamp = Date.now();
}, 0);
},
onError: function(err) {
window.alert(_('Cannot execute %s for %s, because of insufficient permissions', err.command, err.arg), _('Insufficient permissions'), 'alert', 600);
}
}, false, false);
servConn._socket.emit('subscribe', subscriptions);
<!DOCTYPE html>
<html lang="de">
<head>
<link rel="icon" href="data:,">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0" />
<title>TEST</title>
<script src="http://IP:8082/socket.io/socket.io.js"></script>
<script src="../tmp/conn.js"></script>
<script src="moment-with-locales.js"></script>
<script>
moment.locale('de')
</script>
</head>
<body>
<div>
Value: <b id="stromvAktuell">unknown</b>
<br> Last update: <b id="stromvTs">unknown</b>
<br>
</div>
</body>
Related
d3.interval takes two parameters, callback and delay,e.g.
d3.interval(callback, delay).
I was wondering if it is possible to pass on a dynamic delay for each interval.
For example, in the following, I am asking the interval to run at 1000ms delay. But is there a way I can ask d3.interval to run at 0ms, 1000ms, 2000ms, 3000ms respectively for interval# 1,2,3,4.
I tried like desiredDelay[counterF] but it did not work.
const masterSelection = d3.selectAll('[class^="text"]');
const node = masterSelection.nodes();
const len = node.length - 1;
let counterF = 0;
const del = 1000;
const desiredDelay = [0, 1000, 2000, 3000]
let ticker = d3.interval(
e => {
const element = masterSelection['_groups'][0][counterF];
const selection = d3.select(element).node();
console.log(selection);
counterF++;
(counterF > len) ? ticker.stop(): null
}, del
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div class='text1'>one</div>
<div class='text2'>two</div>
<div class='text3'>three</div>
<div class='text4'>four</div>
</body>
<script type="text/javascript" src="prod.js"></script>
</html>
Short answer: you can't.
If you look at the source code you'll see that if the delay is not null...
if (delay == null) return t.restart(callback, delay, time), t;
...it will be coerced to a number using the unary plus operator:
t.restart = function(callback, delay, time) {
delay = +delay,
etc...
What you can do is creating your own interval function, which is out of the scope of this answer.
Adapted from this, the following works as desired and is to be used with d3.timeout.
const masterSelection = d3.selectAll('[class^="text"]');
const node = masterSelection.nodes();
const len = node.length - 1;
let counter = 0;
//const del = 1000;
const delay = [0, 1000, 2000, 3000];
function show() {
const element = masterSelection["_groups"][0][counter];
const selection = d3.select(element).node();
console.log(selection);
counter++;
if (counter > len) return
d3.timeout(show, delay[counter]);
}
show();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div class='text1'>one</div>
<div class='text2'>two</div>
<div class='text3'>three</div>
<div class='text4'>four</div>
</body>
<script type="text/javascript"></script>
</html>
I'm a beginner when it comes to coding and the biggest issue I have is to understand WHY something doesn't work (how to diagnose an error). I tried to combine what I learned from Colt Steele on Udemy with fetch API and so far, I've managed to make it work to list the NAMES of the movies when you search, but when I try to display the IMAGES, they seem to not work and it seems like it's trying to load them from my PC rather than from the TVMaze API. Here's my code:
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
const resultsNames = jsonData.map(element => element.show.name);
const resultsImages = jsonData.map(e => e.show.image);
console.log(resultsNames);
renderResults(resultsNames);
console.log(resultsImages);
renderImages(resultsImages);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderResults(resultsNames) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
resultsNames.forEach(result => {
const element = document.createElement("li");
element.innerText = result;
list.appendChild(element);
});
}
function renderImages(resultsImages) {
const list2 = document.getElementById("imagesDisplay");
list2.innerHTML = "";
resultsImages.forEach(result => {
const imgShow = document.createElement("IMG");
imgShow.src = result;
list2.appendChild(imgShow);
})
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id=" errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>
Can you please help me understand why is this not working and also, how can I make it display in a list like this:
-Name of the show
-Image of the show
-2nd name of the 2nd show
-2nd image of the 2nd show
etc.
Thank you in advance!
If you look at your images, you will see the src as [object Object] instead of the url to your image. You need to access the property of your object, in this case there's a few to choose from that represent different sized images.
I've modified your snippet to get what you want.
function searchShow(query) {
const url = `https://api.tvmaze.com/search/shows?q=${query}`;
fetch(url)
.then(response => response.json())
.then((jsonData) => {
let shows = jsonData.map(element => element.show);
renderShows(shows);
document.getElementById("errorMessage").innerHTML = "";
})
.catch((error) => {
document.getElementById("errorMessage").innerHTML = error;
})
}
function renderShows(shows) {
const list = document.getElementById("resultsList");
list.innerHTML = "";
shows.forEach(show => {
const element = document.createElement("li");
const img = document.createElement("img");
const text = document.createElement("span");
img.src = show.image.original;
text.innerText = show.name;
element.appendChild(text);
element.appendChild(img);
list.appendChild(element);
});
}
let searchTimeoutToken = 0;
window.onload = () => {
const searchFieldElement = document.getElementById("searchField")
searchFieldElement.onkeyup = (event) => {
clearTimeout(searchTimeoutToken);
searchTimeoutToken = setTimeout(() => {
searchShow(searchFieldElement.value);
}, 250);
};
}
img {
max-width: 100px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TV Show</title>
</head>
<body>
<h1>TV Search</h1>
<input type="text" id="searchField" placeholder="Search a TV Show...">
<ul id="resultsList"></ul>
<ul id="imagesDisplay"></ul>
<div id="errorMessage">
</div>
<script src="script.js"></script>
</body>
</html>
I want to compare lastResult value with database qr code value, if both match redirect to a page else show error. My database is phpmyadmin. I'm using laravel 7 but doing this with html and javascript.
<!DOCTYPE html>
<html lang="en">
<head>
<title>QR Code Scanner</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://unpkg.com/html5-qrcode"></script>
</head>
<body>
<div id="qr-reader" style="width:500px"></div><br>
<div id="qr-reader-results" style="padding:10px"></div>
<script>
var resultContainer = document.getElementById('qr-reader-results');
var lastResult, result, countResults = 0;
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
// Handle on success condition with the decoded message.
// console.log (`Scan result ${decodedText}`, decodedResult);
document.getElementById("qr-reader-results").innerHTML= lastResult;
}
}
var html5QrcodeScanner = new Html5QrcodeScanner(
"qr-reader", { fps: 10, qrbox: 250 });
html5QrcodeScanner.render(onScanSuccess);
</script>
</body>
</html>
Thanks M. Eriksson. I found the solution. It is done without ajax request.
function onScanSuccess(decodedText, decodedResult) {
if (decodedText !== lastResult) {
++countResults;
lastResult = decodedText;
// Handle on success condition with the decoded message.
// console.log (`Scan result ${decodedText}`, decodedResult);
// document.getElementById("qr-reader-results").innerHTML= lastResult;
if(lastResult == "{{ auth()->user()->qrcode }}"){
alert("matched");
location.href="/home";
}else{
alert("unmatched");
}
}
}
RXJS countdown timer stops suddenly and i am able find out it is canceled by switchMap when trustworthy data is not emitted.
How to fix it? I need to start the timer by clicking the button.
Please find the jsbin of my example
const start = Rx.Observable.fromEvent(document.querySelector('#btn'), 'click');
start
.switchMap(() => {
return Rx.Observable.timer(0, 1000)
.takeUntil(Rx.Observable.timer(60000 + 1000))
})
.map((value) => 60000 - value * 1000)
.map(function(i) {
return 'Timer (second): ' + i / 1000;
}).subscribe(function(text) {
var container = document.querySelector('#app');
container.textContent = text;
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>RxJS 5 Operators</title>
<script src="https://npmcdn.com/#reactivex/rxjs#5.0.0-beta.3/dist/global/Rx.umd.js"></script>
</head>
<body>
<button id='btn'> start </button>
<p id='app'></p>
</body>
</html>
Try to change takeUntil(...) to take(...)
from:
.takeUntil(Rx.Observable.timer(60000 + 1000))
to:
.take(60000 / 1000 + 1)
Guessing reason:
If timer has accumulated temporal error, takeUntil could be fired before expected timer event occured.
time
[0] 0.000
[1] 1.103
...
[59] 60.234
61.003 <- takeUntil fired
[60] 61.123 <- expected last event lost
const start$ = Rx.Observable.fromEvent(document.querySelector('#btn'), 'click');
start$
.switchMap(()=> {
return Rx.Observable.timer(0, 1000)
.takeUntil(Rx.Observable.timer(3000 + 1000))
.map(value => 3000 - value * 1000)
.map(function (i) {
return 'Timer (second): ' + i/1000;
});
})
.subscribe(function (text) {
var container = document.querySelector('#app');
container.textContent = text;
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>RxJS 5 Operators</title>
<script src="https://npmcdn.com/#reactivex/rxjs#5.0.0-beta.3/dist/global/Rx.umd.js"></script>
</head>
<body>
<button id='btn'> start </button>
<p id='app'></p>
</body>
</html>
I want to change the word in the span tag every 1.5 seconds but so far it is just displaying the last word in the array 'list'.
Here is my javascript
var list = [
"websites",
"user interfaces"
];
setInterval(function() {
for(var count = 0; count < list.length; count++) {
document.getElementById("word").innerHTML = list[count];
}}, 1500);
And here is the html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<span id="word"></span>
</body>
</html>
You don't need a for loop, just use that setInterval, your counter or even simpler using Array manipulation:
var list = [
"websites",
"user interfaces",
"cool neh?"
];
var count = 0; // Separate your count
function changeWord() { // Separate your concerns
document.getElementById("word").innerHTML = list[count];
count = ++count % list.length; // Increment and loop counter
}
changeWord(); // First run,
setInterval(changeWord, 1500); // Subsequent loops
<span id="word"></span>
If you want to not use a counter but do it using array manipulation:
var list = [
"websites",
"user interfaces",
"cool neh?"
];
var ELWord = document.getElementById("word"); // Cache elements you use often
function changeWord() {
ELWord.innerHTML = list[0]; // Use always the first key.
list.push(list.shift()); // Push the first key to the end of list.
}
changeWord();
setInterval(changeWord, 1500);
<span id="word"></span>
P.S: The inverse would be using list.unshift(list.pop()) as you can see here.
Performance-wise the solution using counter should be faster but you have a small Array so the difference should not raise any concerns.
You may wanna try this. Not looping, just calling a changeWord function every 1.5 sec.
var list = [
"websites",
"user interfaces"
];
var count = 0;
function changeWord() {
document.getElementById("word").innerHTML = list[count];
count = count < list.length-1 ? count+1 : 0;
}
setInterval(function() { changeWord(); }, 1500);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<span id="word"></span>
</body>
</html>
I would do this job by setTimeout() as follows,
function loopTextContent(a, el, dur = 1500){
var i = -1,
len = a.length,
STID = 0,
looper = _ => (el.textContent = a[i = ++i%len], STID = setTimeout(looper,dur));
looper();
return _ => STID;
}
var list = ["websites", "user interfaces", "user experience", "whatever"],
getSTID = loopTextContent(list, document.getElementById("word"));
setTimeout(_ => clearTimeout(getSTID()),10000);
<span id="word"></span>
Better use setTimeout. Every iteration should have its own timeout. See also
(() => {
const words = document.querySelector('#words');
typeWords([
"web sites",
"user interfaces",
"rare items",
"other stuff",
"lizard sites",
"ftp sites",
"makebelief sites",
"fake news sites",
"et cetera"
]);
function typeWords(list) {
list.push(list.shift()) && (words.innerHTML = list[list.length-1]);
setTimeout(() => typeWords(list), 1500);
}
})();
<div id="words"></div>
The problem with your code is whenever your interval function is called,loop get executed and prints the element because you are replacing the whole innerHtml on each iteration.
You can try the following code if u want to print whole list element again and again after interval.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<span id="word"></span>
</body>
The javascript code :
var list = [
"websites",
"user interfaces"
];
var count=0;
function print()
{
document.getElementById("word").innerHTML = list[count];
count += 1;
count%=list.length;
}
setInterval( print(), 1000);