Show current state of variable in HTML/JS? - javascript

So I'm developing a webpage and I want to display the value of a variable in a part of it. The variable is being manipulated by many functions and I want the displayed value as the current value of the variable. How exactly do I do that? By the way here's the code. Even if the functions manipulate the variable, it still shows "1"
var cpc = 1;
document.getElementById("curcpc").innerHTML = cpc;

There's nothing that will automatically update the DOM for you when the variable changes. (There are lots of libraries or frameworks that will update the DOM for you if you follow their rules for how you change it, but nothing that will do it completely automatically.)
There are a few ways to do it.
Update function / accessor
One simple way to fix that is to not make it a simple variable. Instead, make setting it something that requires a function call, and have the function call update both the variable and the DOM.
You might make updating it an explicit function call (explicit is usually good!), or you could make it an accessor property with a getter and setter. The upside is you're not going to miss any updates. The downside is it's a function call every time you get or set the "variable"'s value (though with an accessor it doesn't look like one).
Here's a really basic version of that using an accessor:
const data = (() => {
let cpc;
const curcpc = document.getElementById("curcpc");
return {
get cpc() {
return cpc; // Returns the value of the variable
// declared above
},
set cpc(newValue) {
cpc = newValue; // Updates the var above
curcpc.innerHTML = cpc;
}
};
})();
data.cpc = 0;
// Some place updating it
document.getElementById("btn-add").addEventListener("click", () => {
++data.cpc;
});
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", () => {
--data.cpc;
});
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">
But here's a version that's more efficient, not updating the display until the next time the browser is about to paint the page (by asking for a callback via requestAnimationFrame), which means repeated updates that wouldn't have been visible don't get made unnecessarily:
const data = (() => {
let cpc;
const curcpc = document.getElementById("curcpc");
let rAFHandle = 0;
const updateCurCpc = () => {
curcpc.innerHTML = cpc;
rAFHandle = 0;
};
return {
get cpc() {
return cpc; // Returns the value of the variable
// declared above
},
set cpc(newValue) {
cpc = newValue;
if (!rAFHandle) {
rAFHandle = requestAnimationFrame(updateCurCpc);
}
}
};
})();
data.cpc = 0;
// Some place updating it
document.getElementById("btn-add").addEventListener("click", () => {
++data.cpc;
});
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", () => {
--data.cpc;
});
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">
Wrap handlers
Another way is to wrap any event handlers and such in a function that remembers the old value and updates the DOM after the event handler is done if the value has changed. This has the advantage that reading/writing the variable is a simple read/write, not a function call, but adds the cost of doing the comparison after each event handler completes and the possibility of adding a hanlder you forget to wrap.
Here's a basic example of that (without requestAnimationFrame, it doesn't add much in this case):
let cpc = 0;
const curcpc = document.getElementById("curcpc");
curcpc.innerHTML = cpc;
const wrapHandler = (handler) => {
// Return a non-arrow function (we want `this` set by the caller)
return function(...args) {
try {
const result = handler.apply(this, args);
curcpc.innerHTML = cpc;
return result;
} catch (e) {
curcpc.innerHTML = cpc;
throw e;
}
};
};
// Some place updating it
document.getElementById("btn-add").addEventListener("click", wrapHandler(() => {
++cpc;
}));
// Some other place updating it
document.getElementById("btn-subtract").addEventListener("click", wrapHandler(() => {
--cpc;
}));
<div id="curcpc"></div>
<input type="button" value="+" id="btn-add">
<input type="button" value="−" id="btn-subtract">

var cpc = 1;
function updateValue(destination, newValue) {
document.getElementById(destination).innerHTML = newValue;
}
Then you can trigger this update from anywhere you'd like with whatever value you'd like. e.g. usage:
updateValue('curcpc', cpc);

Related

How do i consol.log countRight with this code?

function rightAnswer() {
console.log('right')
var countRight = 0;
[Button2, Buttonb3].forEach(a =>
a.addEventListener('click', () => {
countRight += 1;
})
);
}
I need help with being able to print consol.log(countRight). So far when i use that line of code the consol always shows countRight = 0 even though it should say either 1 or 2 depending on the users input. I need help with making this code work.
It does not work if i put consol.log(countRight) after countRight += 1;
function rightAnswer() {
var countRight = 0;
[Button2, Buttonb3].forEach(a =>
a.addEventListener('click', () => {
countRight += 1;
// this is where you should be able to log the value
console.log(`right value is: ${countRight}`);
})
);
}
This is spaghetti code though. Your function breaks scope by referencing "Button2", "Buttonb3"
If you want to log on clicks the right place to log is in the click handler.
The implication from naming is that countRight will be called to increment the count–as it is no handlers will be added until countRight has been called (and it should be called only once so multiple handlers aren't added).
It might make more sense to add the handlers on the load event. If you really mean to add them at an arbitrary time in the future it can certainly be put in a method, noting again it should only be called once, or the event listeners removed before calling it again.
window.addEventListener('load', () => {
let countRight = 0
const button1 = document.getElementById('button1')
const button2 = document.getElementById('button2')
const buttons = [button1, button2]
const output = document.getElementById('output')
buttons.forEach(b => {
b.addEventListener('click', b => {
countRight++
output.innerText = countRight
console.log(`countRight = ${countRight}`)
})
})
}, false);
<button id="button1">Button #1</button>
<button id="button2">Button #2</button>
<div>Clicked <span id="output">0</span> times.</div>
Tangential
The reason to post a complete example is so people can copy it into their answers and actually run it. It also makes sense to

How do I have an onclick function change the properties of another onclick function?

To help visualize what I'm after. I have a button with an onclick() that increments the value of an input by 1
\\ HTML looks like this
<button class="clickme" onclick="pluspotato()">Potato</button>
<script>
var potatocount = 0;
function pluspotato() {
potatocount = potatocount + 1;
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
</script>
Now I want to add a button that will change the property of the pluspotato() function to multiply by 2.
Any help would be greatly appreciated.
If youd like to solve this properly (so that it scales for further enhancements / development) id suggest you read up on Observables. Im am going to write a simple implementation of one and explain it here.
Since you want to change a value at multiple points in your code and read it potentially at multiple points youd have to come up with some sort of interface that allows participants (eg gui elements) to listen to changes made to it (and uptdate the gui accordingly).
Since this is a quite often needed functionality it is best to write an generally applieable solution for this. Like this Class:
class Observable {
constructor(initalValue) {
// here come the subscriptions / perticepants that want to listen to changes
this.listeners = []
// this is the actual wrapped value. Which can basically be anything. The only
// important thing is that nothing changes this property outside of this class.
// A convention is to mark those properties / functions with an underscore.
this._value = initalValue
}
setValue(value) {
// first check if the current value is not already the same as the new one.
// if so: just do nothing
if (this._value === value) return
// then set the stored value (so that it can be getted)
this._value = value
// loop through all listeners and call them with the now value
for (let i = 0; i < this.listeners.length; i++) {
this.listeners[i](value)
}
}
getValue() {
return this._value
}
subscribe(func) {
// add new listeners to array so that it gets called on setValue
this.listeners.push(func)
// Optional:
// call added function immediately with current value
func(this._value)
}
unsubscribe(func) {
//should also exist
}
}
This class now allows you to add such behaviour.
let observableCounter = new Observable(0)
function incrementClick() {
observableCounter.setValue(observableCounter.getValue() + 1)
}
function doubleClick() {
observableCounter.setValue(observableCounter.getValue() * 2)
}
// then simply listen to changes everywhere you want the gui to update
function update1(value) {
console.log("Updateing GUI to " + value)
// document.getElementById()...
// Side note: dont document.getElementById here. If the element doesnt change,
// getElement once outside update1 and then simply take the reference here.
// This way on every change the element needs to be found from scartch.
}
observableCounter.subscribe(update1)
You can change the element's onclick function to a function that multiplies.
function multpotato() {
potatocount *= 2;
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
document.getElementById("change").addEventListener("click", function() {
document.querySelector(".clickme").onclick = multpotato;
});
You may do conditional operation in pluspotato() depending on activation of the second button:
var potatocount = 0;
var operation = 'add1';
function pluspotato() {
let potatocount;
if(operation === 'multiply2') {
potatocount = Number(document.getElementById("potatonum").value) * 2;
}
else{
potatocount = Number(document.getElementById("potatonum").value) + 1;
}
document.getElementById("potatonum").value = potatocount;
document.title = potatocount + " Potatoes";
}
function changePluspotato() {
operation = 'multiply2';
}
<button class="clickme" onclick="pluspotato()">Potato</button>
<input id="potatonum"></input><br>
<button id="change" onclick="changePluspotato()">changePluspotato</button>
Once you click the second button, the potato button starts to multiply by 2

What's the modern way of catching all changes to the value of an HTML input element?

Is there a standard way of catching all changes to the value of an HTML input element, despite whether it's changed by user input or changed programmatically?
Considering the following code (which is purely for example purposes and not written with good coding practices, you may consider it as some pseudo-code that happens to be able to run inside some web browsers :P )
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
<script>
window.onload = () => {
var test = document.getElementById("test");
test.onchange = () => {
console.log("OnChange Called");
console.log(test.value);
} // Called when the input element loses focus and its value changed
test.oninput = () => {
console.log("OnInput Called");
console.log(test.value);
} // Called whenever the input value changes
test.onkeyup = () => {
console.log("OnKeyUp Called");
console.log(test.value);
} // some pre-HTML5 way of getting real-time input value changes
}
</script>
</head>
<body>
<input type="text" name="test" id="test">
</body>
</html>
However none of those events will fire when the value of the input element is changed programmatically, like someone doing a
document.getElementById("test").value = "Hello there!!";
To catch the value that's changed programmatically, usually one of two things can be done in the old days:
1) Tell the coders to fire the onchange event manually each time they change the input value programmatically, something like
document.getElementById("test").value = "Hello there!!";
document.getElementById("test").onchange();
However for this project at hand the client won't accept this kind of solution since they have many contractors/sub-contractors that come and go and I guess they just don't trust their contractors to follow this kind of rules strictly, and more importantly, they have a working solution from one of their previous contracts which is the second old way of doing things
2) set a timer that checks the input element value periodically and calls a function whenever it's changed, something like this
var pre_value = test.value;
setInterval(() => {
if (test.value !== pre_value) {
console.log("Value changed");
console.log(test.value);
pre_value = test.value;
}
}, 200); // checks the value every 200ms and see if it's changed
This looks like some dinosaur from way back the jQuery v1.6 era, which is quite bad for all sorts of reasons IMHO, but somehow works for the client's requirements.
Now we are in 2019 and I'm wondering if there are some modern way to replace the above kind of code? The JavaScript setter/getter seems promising, but when I tried the following code, it just breaks the HTML input element
Object.defineProperty(test, "value", {
set: v => {
this.value = v;
console.log("Setter called");
console.log(test.value);
},
get: ()=> {
console.log("Getter called");
return this.value;
}
});
The setter function will be called when the test.value is programmatically assigned, but the input element on the HTML page will somehow be broken.
So any idea on how to catch all changes to the value of an HTML input element and call the handler function other than the ancient "use a polling timer" method?
NOTICE: Take note that all the code here are just for example purposes and should not be used in real systems, where it's better to use the addEventListener/attachEvent/dispatchEvent/fireEvent etc. methods
To observe assignments and retrieval to the .value of an element, Object.defineProperty is the way to go, but you need to call the original setter and getter functions inside your custom methods, which are available on HTMLInputElement.prototype:
const { getAttribute, setAttribute } = test;
test.setAttribute = function(...args) {
if (args[0] === 'value') {
console.log('Setting value');
}
return setAttribute.apply(this, args);
};
test.getAttribute = function(...args) {
if (args[0] === 'value') {
console.log('Getting value');
}
return getAttribute.apply(this, args);
};
test.setAttribute('value', 'foo');
console.log(test.getAttribute('value'));
<input type="text" name="test" id="test">
Note the use of methods rather than arrow functions - this is important, it allows the this context to be preserved. (you could also use something like set.call(input, v), but that's less flexible)
That's just for changes to .value. You can monkeypatch something similar for setAttribute('value, if you want:
const { setAttribute } = test;
test.setAttribute = function(...args) {
if (args[0] === 'value') {
console.log('attribute set!');
}
return setAttribute.apply(this, args);
};
test.setAttribute('value', 'foo');
<input type="text" name="test" id="test">
The standard way is to not fire a change event when the value has been changed programmatically.
Not only are there too many ways to set the value of an input programmatically, but moreover, that's just a call for endless loop.
If in any of your callbacks your input's value is set, then you'll crash the page.
Wanna try?
let called = 0; // to avoid blocking this page
const { set, get } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
Object.defineProperty(inp, 'value', {
set(v) {
const ret = set.call(this, v);
if(++called < 20) // I limit it to 20 calls to not kill this page
this.dispatchEvent(new Event('all-changes'));
return ret;
},
get() { return get.call(this); }
});
inp.addEventListener('all-changes', e => {
inp.value = inp.value.toUpperCase();
console.log('changed');
});
btn.onclick = e => inp.value = 'foo';
<input id="inp">
<button id="btn">set value</button>
So the best is still to only call whatever callback directly from the code responsible of the change.

How to access variables from a function inside executed inside another function?

I am refactoring my https://github.com/perdugames/cacau test library, which I did to learn a bit more about JavaScript and testing. The problem is that I'm not liking how FIXTURE are currently working on it, so I decided to refactor, and I wanted to get the result below, but I still do not know how to make the "addTest" functions have access to "x".
const runner = new Runner();
runner.addSuite(() => {
console.log('Suite 1');
runner.addFixture(() => {
console.log('Suite');
const x = 10;
});
runner.addTest(() => {
console.log('Test 1');
console.log(x);
});
runner.addTest(() => {
console.log('Test 2');
console.log(x);
});
});
runner.run();
Note: Of course without the use of globals, and without adding to the scope of "runner".
Actually, you should be using contexts. Please read more about contexts in javascript. There's plenty of sites.
In your case you can call a context
obj.funcA(() => {
let ctx = this;
console.log('A');
obj.funcA1(() => {
let ctxFncA1 = this // context of previous level... etc.
console.log('A1');
const x = 10;
});
// ... you get the point.
});
In general what you're trying to do is not "ok". If you go build object class, the JavaScript language might allow you to do really anything, but you must not.
Probably you should look into object programmation in JavaScript (book or training website).
Actually you have an example describing pretty much what you desire on : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let (Code is copy/pasted from there.)
var Thing;
{
let privateScope = new WeakMap();
let counter = 0;
Thing = function() {
this.someProperty = 'foo';
privateScope.set(this, {
hidden: ++counter,
});
};
Thing.prototype.showPublic = function() {
return this.someProperty;
};
Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}
console.log(typeof privateScope);
// "undefined"
var thing = new Thing();
console.log(thing);
// Thing {someProperty: "foo"}
thing.showPublic();
// "foo"
thing.showPrivate();
// 1
I do not believe this is physically possible. You would have to pass the the variable to the function in order to access it if you do not want a global variable, or have it accessible in the scope of a class or object.
While this variable should be in your heap, there is no way to communicate its address to the scope of the new function without passing something to it, either a pointer or the value itself.
I found two ways to resolve the issue, the first was:
function test(){
console.log(this.x * 2);
}
function fixture() {
this.x = 5;
}
test.call(new fixture()) // 10
This is the worst way I found, since I will always have to use "this", and I will still have to deal with FIXTURE as a constructor function, otherwise it would be better to use a literal object in this case.
The second way I chose it is simpler, and it already fits in with what I have ready:
const runner = new Runner();
runner.addSuite(() => {
console.log('Suite 1');
var x;
runner.addFixture(() => {
console.log('Suite');
x = 1;
x++;
});
runner.addTest(() => {
console.log('Test 1');
console.log(x); // 2
});
runner.addTest(() => {
console.log('Test 2');
console.log(x); // 2
});
});

Javascript object this is alway window

I've asked this in the past but still not understanding why my this is always window and not the calling object.
on the page there is button control:
<input type="button" value="Press Me" id="buttonPressMe" #click="pressMeClicked" />
This is the component and under methods is where I have functions:
pressMeClicked() is the function that gets called:
pressMeClicked: () => {
console.log(this.el)
var dd = this;
console.log('pressMeClicked');
}
The problem is this is not the component object in the pressMeClicked() function. It's always the window object. The code below shows how I add an event listener to the element. And either of the two options I used does call the pressMeClicked function. The only problem is that this is not the component obj but window:
const funcBind = function(obj, method, args = []){
return function(){
return method.apply(obj, args);
}
}
const funcProcess = (component, elements) => {
const fn = component.methods['pressMeClicked'];
const methodCall = funcBind(component, fn);
const elem = component.selector('#buttonPressMe');
elem.addEventListener('click', () => {
// Tried this
fn.bind(component)()
// Tried this also
methodCall();
}
}
When I step into the function methodCall(), obj is the component.
Any help would greatly be appreciated
pressMeClicked is an arrow function, which is a non-bound function. Because of that, this will always refer to the value of this inside the lexical scope of its containing function. To fix this change the arrow function definition to a regular function (method):
// Instead of:
pressMeClicked: () => {
// ...
}
// Do this:
pressMeClicked() {
// ...
}
// or:
pressMeClicked: function() {
// ...
}

Categories

Resources