Persist variables instead of global variables in JavaScript - javascript

I want to have one button, functioning as both the "start" and "stop" button for some reoccurring timed events.
To do this, I can have a global variable on the top of everything:
toggleOn = false;
And then, inside of <button onClick="..., I can have:
toggleOn =! toggleOn;
foo();
function foo() {
// do my stuff
if (toggleOn) {
setTimeout(foo, 5000);
}
}
But the problem is, I must not use a global variable to complete the same task. How should I do it? Is there a persist variable that can carry a value outside its scope?

This is an example for something where closures are great feature of the language.
(function()
{
var active = false;
myButton.addEventListener('click', function myButtonClick(event)
{
if (active) {
// recursion..?
setTimeout(myButtonClick, 5000);
}
active = !active;
}
})();
More on closures here.

Use the javascript module pattern. Something like this:
var handler = function () {
var private_state = true;
return function() {
private_state = !private_state;
if (private_state) {
// Do something
}
}
}();
Use handler as your button onclick handler.

Earlier answers already noted that you could use closures to store a "private" variable that would keep track of state. Alternatively you could use HTML5 data to store this as well.
html
<button data-toggleOn="false">Click me!</button>
javascript
button.addEventListener('click', function() {
var toggleOn = this.dataset.toggleOn = !JSON.parse(this.dataset.toggleOn);
if (toggleOn) {
// do stuff!
}
});
and if you're using jQuery..
$('button').click(function() {
var toggleOn = !$(this).data('toggleOn');
$(this).data('toggleOn', toggleOn);
});

Related

Passing variable that detects specific button in jQuery

I created function that recognize which post has been clicked on.
jQuery(document).ready(function() {
jQuery(.recognize-post).on("click", function() {
var clickedButton = jQuery(this).data("id")
console.log("click button with post id: ", clickedButton)
button-id = "recognize-post"
...
...
})
})
}
html
<button id="recognize-post" class="recognize-post" data-id="<?php the_title() ?>">POST</button>
Code above works perfectly and in recognizes the correct post, but I need to pass clickedButton outside of this function and I don't know how to do so.
I need to have it in else if function, this is my attempt
else () {
...
} else if (button-id === "recognize-post") {
console.log(clickedButton)
}
Here the problem comes, clickedButton is underfined and need it to recognize post in exactly the same way how in on click function. Is it possible?
You can make a separate function that takes in the information you want to preserve.
// make a new function
function doSomethingWithTheIdAndBtn(id, btn) {
// take in arguments that represent the id or btn or whatever you need
else () {
...
} else if (id === "recognize-post") {
console.log(btn)
}
}
jQuery(document).ready(function() {
jQuery(.recognize-post).on("click", function() {
var clickedButton = jQuery(this).data("id")
console.log("click button with post id: ", clickedButton)
button-id = "recognize-post"
doSomethingWithTheIdAndBtn(button-id, clickedBtn) // call the function
...
...
})
})
}
So, the issue here is that if you declare a variable function in a given "scope" — in your case, the anonymous function's scope — it will only be defined inside of that scope. If you want to use the variable outside of the function, you need to declare it outside of the function.
So, for instance, if your code was
function foo() {
var myVariable = 0;
}
foo();
// This will throw an error, cuz myVariable is not defined in this scope
console.log(myVariable);
you could fix it by declaring the variable outside of the function's scope
var myVariable; // declare it outside of the function
function foo() {
myVariable = 0; // give it a value inside of the function
}
foo(); // call foo so that myVariable has a value
console.log(myVariable); // this will print 0. Success!

Hide function using block scope

In the past, if I want to hide a variable function, I can using IIFE
(function(){
var _hiddenVariable = "_hiddenVariable";
function _hiddenFunction(){
//blablabla
}
})();
console.log(_hiddenVariable); //error
_hiddenFunction(); //error
After ES6, I can use let and block scope to hide a variable, but function can't.
{
let _hiddenVariable = "_hiddenVariable";
function _hiddenFunction(){
//blablabla
}
}
console.log(_hiddenVariable); //error
_hiddenFunction(); //pass
Below is the only solution I can find.
{
let _hiddenFunction = function(){
//blablabla
}
}
_hiddenFunction(); //error
So, two questions.
Is there another way to hide a function in block scope?
Is it a good idea that I use the solution above in all my projects? If not, can you suggest me a solution better than this?
thx
Well, you can hide the function by returning only what you want from an object.
Some
class Character {
constructor (name, weapon){
this.name=name;
this.weapon=weapon;
}
attack = function(){
var that=this;
that.varToSee=54
const age=()=>{
return that.varToSee
}
that.receivedFunc=function (){
return "Attack";
}
return {
age: age
}
}
}
const shrek=new Character("Shrek", "cloth")
const receive =shrek.attack()
console.log(receive.age())
console.log(receive.receivedFunc())
You might see that receivedFunc will not be accessible.

Dynamic Function Call for Prototype Functions

I have to make a bunch of .prototype declarations within a function and would like to add some dynamism to reduce the size of my code.
Here is some pseudo code for what I need to do:
window.myClass = function(){
var object_type = getObjectType();
if (object_type === 'NodeList')
{
NodeList.prototype.func_name = function(){
//some code
}
}
if (object_type === 'HTMLCollection')
{
HTMLCollection.prototype.func_name = function(){
//the same code again
}
}
}
I would like to change this so I can make these declarations dynamic, kind of like this:
window.myClass = function(){
var object_type = getObjectType();
object_type.prototype.func_name = function(){
//some code
}
}
Is this possible?
EDIT
I forgot to mention that I would love to keep all my functions within the scope of window.myClass
In your case you can simply do
window[object_type].prototype[func_name] = function(){...
But be careful that you seem to be engaged in modifying objects you don't own. There's probably a better possible design for your application.
Without going into details about what are you trying to accomplish or whether it's a good idea or there are better ways to do it, just based on your posted code, all you need to do is define "some code" as a funtion and assign it to whatever you want:
window.myClass = function(){
function someCode() { /* some code */ }
var object_type = getObjectType();
if (object_type === 'NodeList')
{
NodeList.prototype.func_name = someCode;
}
if (object_type === 'HTMLCollection')
{
HTMLCollection.prototype.func_name = someCode;
}
}
But you don't really need the if statement, because you can just do
window.myClass = function(){
function someCode() { /* some code */ }
var object_type = getObjectType();
window[object_type].prototype.func_name = someCode;
}

js:scope of this inside an object on keypress event

Here is my JS simple script:
var Chat = function() {
console.log("init");
this.debug = function (txt) {
console.log(txt);
}
document.getElementById('shoutBoxInput').addEventListener("keypress", keyPressedFunction, false);
this.keyPressedFunction = function(e){
console.log("keyPressed");
}
this.sendText = function() {
var texte = document.getElementById('shoutBoxInput').value;
if (texte=="") return;
document.getElementById('shoutBoxInput').value =""
this.debug("sendTexte:"+texte);
}
this.receiveText = function(username, texte) {
}
}
var chat = new Chat();
My problem comes from:
document.getElementById('shoutBoxInput').addEventListener("keypress", keyPressedFunction, false);
this.keyPressedFunction = function(e){
Error Uncaught ReferenceError: keyPressedFunction is not defined
If I use:
document.getElementById('shoutBoxInput').addEventListener("keydown", this.keyPressedFunction, true);
then keyPressedFunction is never called.
Fiddle: http://jsfiddle.net/ghLfhb6z/
Let's start with the problem, and then move to what's dangerous about your code.
The problem is that when you call addEventListener, this.keyPressedEvent doesn't yet exist:
// this.keyPressedFunction doesn't exist...so you are registering a 'keypress'
// event to undefined.
document.getElementById('shoutBoxInput').addEventListener("keypress",
keyPressedFunction, false);
// now you define this.keyPressedFunction
this.keyPressedFunction = function(e){
console.log("keyPressed");
}
// so this is where you should be attaching it to the event
You may be thinking about JavaScript's hosting mechanism, and thinking "ah, the this.keyPressedFunction definition is being hoisted to the top of this function, so it's available for assigment." But hoisting only applies to variable and function definitions; what you're doing is assigning an anonymous function to a member property, so hoisting does not apply.
Now on to the dangerous:
When you use a method (a function property of an object) for a callback, the meaning of this is lost when that callback is invoked. (I know you aren't currently using this in your callback, but you probably will eventually!) In other words, when a key is pressed, and keyPressedFunction is called, the value of this won't be what you expect. The upshot of this is you have to be very careful assigning methods to callbacks or events. If you want to do it, you'll have to use Function.prototype.bind. Here's your code re-written in the correct order, and using bind:
this.keyPressedFunction = function(e){
console.log("keyPressed");
}
document.getElementById('shoutBoxInput').addEventListener("keypress",
this.keyPressedFunction.bind(this), false);
place your function before you use its referenc...then use this.keyPressedFunction...then is 'keypress' a valid native js event ?
http://jsfiddle.net/ghLfhb6z/4/
yes there was the errors I told, in fact most important is to place your event handlers at the end, check the right event, and use this if the function is on this :
var Chat = function() {
console.log("init");
this.debug = function (txt) {
console.log(txt);
}
this.keyPressedFunction = function(e){
console.log("keyPressed");
}
this.sendText = function() {
var texte = document.getElementById('shoutBoxInput').value;
if (texte=="") return;
document.getElementById('shoutBoxInput').value =""
this.debug("sendTexte:"+texte);
}
this.receiveText = function(username, texte) {
}
// place this at the end
document.getElementById('shoutBoxInput').addEventListener("keydown", this.keyPressedFunction, false);
}
var chat = new Chat();
#dmidz has provided a correct answer that will solve your problem, but if your keyPressedFunction only needs to be referred to code inside your Chat() module, then you don't need to make them properties of this (Chat):
var Chat = function() {
console.log("init");
function debug(txt) {
console.log(txt);
}
function keyPressedFunction(e){
console.log("keyPressed");
}
this.sendText = function() {
var texte = document.getElementById('shoutBoxInput').value;
if (texte=="") return;
document.getElementById('shoutBoxInput').value ="";
debug("sendTexte:"+texte);
}
this.receiveText = function(username, texte) {
}
document.getElementById('shoutBoxInput')
.addEventListener("keypress", keyPressedFunction, false);
}
If you do this, then you don't necessarily have to declare your functions before you use them, but it would be good style to do so nonetheless.

javascript prototype class, this in jquery click

I made a javascript prototype class.
Inside a method I create an jquery click.
But inside this click I want to execute my build function.
When I try to execute a prototype function inside a jquery click it fails because jquery uses this for something else.
I tried some different things, but I couldnt get it working.
Game.prototype.clicks = function(){
$('.flip').click(function(){
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
$(this).find('.card').addClass('flipped');
cardsPlayed.push($(this).find('.card').attr('arrayKey'));
console.log(cardsPlayed[cardsPlayed.length - 1]);
console.log(playingCards[cardsPlayed[cardsPlayed.length - 1]][0]);
if(cardsPlayed.length == 2)// two cards played
{
if(playingCards[cardsPlayed[0]][0] == playingCards[cardsPlayed[1]][0])
{ // same cards played
console.log('zelfde kaarten');
playingCards[cardsPlayed[0]][0] = 0; //hide card one
playingCards[cardsPlayed[1]][0] = 0; //hide card two
//rebuild the playfield
this.build(); //error here
}
else
{
//differend cards
}
}
}
return false;
}).bind(this);
}
The problem is that you're trying to have this reference the clicked .flip element in $(this).find('.card') as well as the Game object in this.build(). this can't have a dual personality, so one of those references needs to change.
The simplest solution, as already suggested by Licson, is to keep a variable pointing to the Game object in the scope of the click handler. Then, just use this inside the handler for the clicked element (as usual in a jQuery handler) and use self for the Game object.
Game.prototype.clicks = function() {
// Keep a reference to the Game in the scope
var self = this;
$('.flip').click(function() {
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
// Use this to refer to the clicked element
$(this).find('.card').addClass('flipped');
// Stuff goes here...
// Use self to refer to the Game object
self.build();
}
}); // Note: no bind, we let jQuery bind this to the clicked element
};
I think you want something like this:
function class(){
var self = this;
this.build = function(){};
$('#element').click(function(){
self.build();
});
};
If I understand correctly, in modern browsers you can simply use bind:
function MyClass() {
this.foo = 'foo';
$('selector').each(function() {
alert(this.foo); //=> 'foo'
}.bind(this));
}
Otherwise just cache this in a variable, typically self and use that where necessary.

Categories

Resources