RxJS detect when observable has been subscribed to - javascript

I have a need to detect when an observable (observedEvents) has been subscribed to, and then subscribe to another observable (triggerEvent). I don't want to subscribe to triggerEvent manually, but only once and when observedEvents has a subscription.
Here is some code explaining what I am looking for:
// This just emits events
let emitting = new EventEmitter();
// This is the main Observable which someone else might
// have access to
let observedEvents = Rx.Observable.merge(
Rx.Observable.fromEvent(emitting, 'aba'),
Rx.Observable.fromEvent(emitting, 'bob')
)
// This trigger should get a subscription if observedEvents
// has one, i.e. when I subscribe to observedEvents
// that subscription activates this trigger
// I have made an attempt at this by calling skipUntil
// this however skips one event, but I don't want that
let triggerEvent = Rx.Observable.merge(
// these actions are things that can
// happen when the trigger is active
Rx.Observable.of('a').delay(200),
Rx.Observable.of('b').delay(400),
Rx.Observable.of('c').delay(600)
)
.skipUntil(observedEvents);
// Something else should be used to activate trigger
// I don't want to do this part manually
triggerEvent.subscribe(val => {
console.log(`Do something fancy with ${val}`);
});
//----------------------------------------------------
// Somewhere else in the code...
//----------------------------------------------------
observedEvents.subscribe(evt => {
console.log(`Some event: ${evt}`);
});
// At this point I want triggerEvent to become active
// because observedEvents has a subscription
setTimeout(() => {
emitting.emit('bob', 'world');
setTimeout(() => emitting.emit('aba', 'stackoverflow!'), 500);
}, 200);
<!DOCTYPE html>
<html>
<head>
<script src="https://npmcdn.com/#reactivex/rxjs#5.0.0-beta.7/dist/global/Rx.umd.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.1.0/EventEmitter.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html>
Is this possible?
I hope that explains what I'm looking for.
As I'm writing this, I'm thinking a solution with Subjects is probably what I need. I'm not sure, but I just need a nudge in the right direction or a solution if possible.

For rxjs > v7, see this answer
Answer
Sure enough I was right about using Subjects. The key was the observers list for Subject. Here is what I finally did:
let emitting = new EventEmitter();
let sub = new Rx.Subject();
// return this to users
let myGlobalSub = sub.merge(Rx.Observable.of(1, 2, 3));
// For internal use
let myObservers = Rx.Observable.fromEvent(emitting, 'evt');
console.log(`The number of subscribers is ${sub.observers.length}`);
// Only do something if myGlobalSub has subscribers
myObservers.subscribe(l => {
if (sub.observers.length) { // here we check observers
console.log(l);
}
});
// Somewhere in the code...
emitting.emit('evt', "I don't want to see this"); // No output because no subscribers
myGlobalSub.subscribe(l => console.log(l)); // One sub
emitting.emit('evt', 'I want to see this'); // Output because of one sub
console.log(`The number of subscribers is ${sub.observers.length}`);
<!DOCTYPE html>
<html lang=en>
<head>
<script src="https://unpkg.com/rxjs#5.5.11/bundles/Rx.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.2.5/EventEmitter.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html>

The answer by #smac89 use the observersproperty which has been deprecated in RxJS 7 and is scheduled to be removed in version 8.
If you only want to know if an Observable has been subscribed to, you may use the observed boolean property instead.
There is currently no real equivalent to the observers array.
let emitting = new EventEmitter();
let sub = new rxjs.Subject();
// return this to users
let myGlobalSub = rxjs.merge(sub,rxjs.of(1, 2, 3));
// For internal use
let myObservers = rxjs.fromEvent(emitting, 'evt');
console.log(`Has the subject been subscribed to ? ${sub.observed}`);
// Only do something if myGlobalSub has subscribers
myObservers.subscribe(l => {
if (sub.observed) { // here we check observers
console.log(l);
}
});
// Somewhere in the code...
emitting.emit('evt', "I don't want to see this"); // No output because no subscribers
myGlobalSub.subscribe(l => console.log(l)); // One sub
emitting.emit('evt', 'I want to see this'); // Output because of one sub
console.log(`Has the subject been subscribed to ? ${sub.observed}`);
<!DOCTYPE html>
<html lang=en>
<head>
<script src="https://unpkg.com/rxjs#%5E7/dist/bundles/rxjs.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/EventEmitter/5.2.5/EventEmitter.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html>

Related

How do I make an element disappear after a set amount of time?

i am trying to make the element with ID thanks1 disappear after a set amount of time. So far I have tried many things like setTimeout but none have worked
<!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>test</title>
<script src="scripts.js"> </script>
</head>
<body>
<h1> 1 </h1>
<p> QUESTION </p>
<button onclick="rightAnswer()" id="a"> PLACE HOLDER </button>
<button onclick="wrongAnswer()"> PLACE HOLDER </button>
<p id="output"> </p>
<p id="thanks1"> Well Done </p>
</body>
</html>
This is a quiz and it gets you to the next page when you get a question correct, I tried putting a timeout in the rightAnswer function so as soon as you load 1.html the Well Done disappears after some time.
const wrong = "Wrong, try again!";
function wrongAnswer() {
document.getElementById('output').innerHTML = wrong
}
function rightAnswer() {
if (document.getElementById('a')) {
location.href = "1.html";
setTimeout(document.getElementById('thanks1').style.display = "none", 3000)
}
if (document.getElementById('b')) {
location.href = "2.html"
}
if (document.getElementById('c')) {
location.href = "3.html"
}
}
function goBack() {
if (document.getElementById('b')) {
location.href = "index.html"
}
if (document.getElementById('c')) {
location.href = "1.html"
}
if (document.getElementById('d')) {
location.href = "2.html"
}
}
I wanted to make the element with the ID "thanks1" disappear once its corresponding page has loaded and after a set amount of time. I tried using setTimeout but nothing that I wanted seemed to happen.
Your syntax is incorrect.
MDN Docs: setTimeout()
From the docs:
The global setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires.
setTimeout(functionRef, delay)
// Parameters
// functionRef - A **function** to be executed after the timer expires.
// delay - The time, in milliseconds that the timer should wait before
// the specified function or code is executed.
For simplicity, I used the same 'thanks1' container for outputting the values
function wrongAnswer() {
// "wrong" should be a string unless you're calling a variable
document.getElementById('thanks1').innerHTML = "wrong";
}
function rightAnswer() {
if (document.getElementById('a')) {
location.href = "1.html";
setTimeout(wrongAnswer, 3000)
}
...
}
Example:
https://codepen.io/carbonspace/pen/QWxwvRo

Jsdom load javascript code but doesn't run correctly

What is wrong with this code? It displays the says: hello bar from the out.js console.log but does not run the rest of the script doesn't add the link inside <div id="link"></div>
If I put the script directly in the code it works, but not in a .js file
teste2.js
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const dom = new JSDOM(`
<!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>
<body>
<div id="link"></div>
<p>Hello world</p>
<script src="http://localhost/2022/jsdom/out.js"></script>
</body>
</html>`, { resources: "usable", runScripts: "dangerously"});
const document = dom.window.document;
console.log(document.getElementsByTagName('html')[0].innerHTML);
out.js
let T = document.getElementById('link');
T.innerHTML = 'LINK';
console.log('bar says: hello');
JSDOM loads sub-resources asynchronously, so your Node.js code is accessing the DOM before the <script> code has executed.
This is also why the log of document.getElementsByTagName('html')[0].innerHTML appears before the log of 'bar says: hello'.
You can handle this by explicitly waiting for the load event:
const document = dom.window.document;
document.addEventListener('load', () => {
console.log(document.getElementsByTagName('html')[0].innerHTML);
});
You'll need more complex logic if the script itself does anything asynchronously. Firing a custom event that you can listen for is a good approach.

How do I connect an audio worklet to a web page?

I'm trying to connect a web page to an audio worklet (following this demos but I got stuck.
Can somebody help?
Here's the code I've got so far.
Those files are all in the same folder, but they don't log anything.
INDEX.HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
INDEX.JS
const demoCode = async () => {
const audioContext = new AudioContext()
await audioContext.audioWorklet.addModule('test-processor.js')
const testNode = new AudioWorkletNode(audioContext, 'test-processor')
testNode.connect(audioContext.destination)
}
TEST-PROCESSOR.JS
class TestProcessor extends AudioWorkletProcessor {
constructor () {
super()
console.log(currentFrame)
console.log(currentTime)
}
process (inputs, outputs, parameters) {
return true
}
}
console.log(sampleRate)
const usefulVariable = 42
console.log(usefulVariable)
registerProcessor('test-processor', TestProcessor)
It looks like you're not invoking your demoCode() function anywhere. If you want to be compliant with the autoplay policy in todays browsers that needs to happen in response to a user gesture.
First you need to add a button to your HTML.
<button id="start" type="button">start</button>
Then you can attach an event listener for that button within your index.js file.
document.getElementById('start').addEventListener('click', demoCode);

Passing onclick event into template literals without global function

I've been working on adding onclick event in template literals with plain javascript (without jquery only javascript). As you can see the result html knows that onclick event on each div has function which will alert as I click but when I click, it didn't respond. It seems like suppose to work but somehow it is not working.
I got lots of help from Stackoverflow but most of the anwser was using global function. but I don't personally want to use global function since sometimes it cause some trouble.
so how can I actually pass the function into onclick event by using template literals?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="ul"></div>
</body>
<script src="/index.js"></script>
</html>
index.js
function test() {
const list = [
{ number: 1, check: () => alert("1") },
{ number: 2, check: () => alert("2") },
{ number: 3, check: () => alert("3") },
];
const $list = list.reduce((acc, item) =>
acc + `<div onclick='${item.check}'>${item.number}</div>`,""
);
const $ul = document.querySelector("#ul");
$ul.innerHTML = $list;
}
test();
result
Instead of building the DOM via HTML strings, create actual DOM elements.
const $ul = document.querySelector("#ul");
for (const item of list) {
const element = document.createElement('div');
element.textContent = item.number;
element.onclick = item.check;
$ul.appendChild(element);
}

observing Attribute change in a div

I am trying to observe a change to
<div id="one" class="elem" data-result="inr"></div>
data-result attribute in this div.
Most of the older methods seem to have been depreciated. I realised that MutationObserver is the best way to proceed. and wrote one on my own which did not work. I found the onattrchange.js which is similar.
With this I am trying to solve the problem I face.
I have created this jsfiddle which is able to detect input changes (using $(document).on), but not the changes to data-result attribute.
I have tried two ways... (1) ('body').attrchange
(2) $(document).on('attrchange',...)
I am not sure why this does not work. ( I am also pretty new to js and jquery, learning by coding)
edit: I have found a similar fiddle which does exactly what I want to. I am doing something similar but I guess I have made some small mistake.
I found the problem with the code.
The onattrchange.js does not seem to identify changes made using
$("element").data("result",$(this).val());
It detects
$("element").attr("data-result",$(this).val());
I have added the working fiddle.
I was looking for same solution, but didn't wanted to use any 3rd party library.
After digging out little bit on google as well as on this site, I found solution on https://stackoverflow.com/a/58501827/6879925
// code to change image src in each 1000ms.
count = 0;
setInterval(function () {
var sourceElement = document.querySelector('#div1');
sourceElement.setAttribute('data-src', 'something' + count);
sourceElement.innerHTML = 'something' + count;
count++;
}, 2000);
function startMutationObserver(tNode, c) {
// Select the node that will be observed for mutations
const targetNode = tNode ? tNode : document;
// Options for the observer (which mutations to observe)
const config = c ? c : {
attributes: true,
childList: true,
subtree: true
};
// Callback function to execute when mutations are observed
const callback = function (mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
targetNode.dispatchEvent(new CustomEvent('newChild', {
detail: mutation
}));
} else if (mutation.type === 'attributes') {
targetNode.dispatchEvent(new CustomEvent('attributeChange', {
detail: mutation
}));
}
}
}
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
// Later, you can stop observing
// observer.disconnect();
}
// call this function to start observing DOM element change
startMutationObserver(document);
// code to listen custom event and filter custom event as per requirement
document.addEventListener('attributeChange', function (e) {
// console.log(e);
const ele = e.detail;
if (ele.target.matches('div[data-src]') && ele.attributeName == 'data-src') {
var src = e.detail.target.getAttribute('data-src');
console.log('new src=', src);
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id='div1' data-src="something">Something....</div>
</body>
</html>
I think this is one of the best solution I ever got for attribute change detection in HTML/javaScript.

Categories

Resources