Having an Issue Defining Variable Globally In jquery - javascript

I'm trying to keep what I save in local storage on the page, that way if I were to refresh I would still have whatever I entered in the textarea box.
However I'm not sure if it's because I am using a variable that assigns to e.target then the relative path to what I am trying to save, but when I console.log my variable it says it's not defined and I'm assuming that my issue is because I don't fully understand jQuery.
function updateHours() {}
function handleSave(e) {
var value = $(e.target).siblings('.description').val()
var hour = $(e.target).closest('.time-block').attr('id')
localStorage.setItem(hour, value)
}
function main() {
updateHours();
$(document).on('click', '.saveBtn', handleSave)
console.log(localStorage.getItem(value))
}
$(document).ready(main)

Try something like this:
function updateHours(){
document.querySelectorAll('.time-block').forEach(el => {
const savedValue = localStorage.getItem(el.id);
const description = el.querySelector('.description');
if (savedValue && description) {
console.debug("Load value", el.id, savedValue);
description.value = savedValue;
}
});
}
function handleSave(e){
const el = e.target.closest('.time-block');
if (!el) { return; }
const description = el.querySelector('.description');
if (!description) { return; }
console.debug("Save value", el.id, description.value);
localStorage.setItem(el.id, description.value);
}
function main(){
updateHours();
document.addEventListener('click', e => {
if (!e.target.classList.contains('saveBtn')) { return; }
handleSave(e);
});
}
window.addEventListener('DOMContentLoaded', main);

Related

When I give the function a name and trigger it from the console it works perfectly, but when I add the window.onload = to it it never starts

This is my code:
window.onload = function ()
{
var user = firebase.auth().currentUser
var login = document.getElementById("NavLogin");
if (user) {
login.innerHTML = "Account";
login.href = "AccountPage/AccountPage.html";
}
else {
login.innerHTML = "Login";
login.href = "AccountPage/LoginPage.html"
}
}
I have tried changing it to the onload of the body, but that didn't help either. I do not understand why it only works if called manually.
From your code I can see that you need to wait until #NavLogin element is loaded. If this element needs to be present in your page to run the rest of the code, then I would suggest you to use a pulling function like bellow:
function waitForElement(selector, cb) {
var tick = setInterval(function () {
var target = document.querySelector(selector);
if (null !== target) {
clearInterval(tick);
cb(target);
}
}, 200);
};
Then you can call the pulling function and pass your target element as the first parameter:
waitForElement("#NavLogin", function(login){
if (user) {
login.innerHTML = "Account";
login.href = "AccountPage/AccountPage.html";
}
else {
login.innerHTML = "Login";
login.href = "AccountPage/LoginPage.html"
}
})
If the target element is found, the callback function will be called and the target element will be passed as parameter to the callback function

JS functions and abstract class and prototypes

Here is a simplified version of my code :
function TextBox () {
this.builddom = function () {
// Building the text dom
}
}
function ImageBox () {
this.builddom = function () {
// Building the image dom
}
}
function Box (type) {
var handler =
(type == 'text') TextBox :
(type == 'Image') ImageBox : null;
if (handler) (handler).call (this);
this.builddom = function () {
// Here I would like to call the correct builddom function for the type.
}
}
var textbox = new Box ('text');
textbox.builddom ();
If Box.builddom doesn't exists, this works fine, the builddom function associated with the specific type is called. But I need to do some general thing in Box and then call the specific builddom. If I give a different name to Box builddom, say Box.dobuilddom, it is fine too, but breaks generic access to Boxes.
I think some clever prototype manipulation can do the job, but I was unable to find it.
Maybe would be better to avoid prototyping and use composition instead:
function TextBox(box) {
this.builddom = function() {
console.log('Building the text dom', box.props);
}
}
function ImageBox(box) {
this.builddom = function() {
console.log('Building the image dom', box.props);
}
}
function Box(props) {
this.props = props;
this.builddom = function() {
throw new Error('unsupported function');
}
}
var textbox = new TextBox(new Box({size:5}));
textbox.builddom();
I don't really understand the concept. The box is just some sort of container. It does not do anything but creates a new instance. What you'd really need here is a Box interface, but js does not have interfaces. You can use TypeScript if you want to...
function TextBox () {
this.builddom = function () {
// Building the text dom
}
}
function ImageBox () {
this.builddom = function () {
// Building the image dom
}
}
var container = {
createBox: function (type){
if (type == "text")
return new TextBox();
else if (type == "image")
return new ImageBox();
else
throw new Error();
}
};
var textbox = container.createBox('text');
textbox.builddom();
Another option is using proxy if you want to wrap objects, but I don't think that's your goal here.
If you need type check later, then you can use inheritance, but there is no multi inheritance, so even that way you cannot imitate interfaces. It goes this way btw.
function Box (){}
function TextBox () {}
TextBox.prototype = Object.create(Box.prototype, {
constructor:TextBox,
builddom: function () {
// Building the text dom
}
});
function ImageBox () {}
ImageBox.prototype = Object.create(Box.prototype, {
constructor:ImageBox,
builddom: function () {
// Building the image dom
}
});
var container = {
createBox: function (type){
if (type == "text")
return new TextBox();
else if (type == "image")
return new ImageBox();
else
throw new Error();
}
};
var textbox = container.createBox('text');
console.log(
textbox instanceof Box,
textbox instanceof ImageBox,
textbox instanceof TextBox
);
textbox.builddom();
If you want use prototyping, you can smth like this:
function TextBox(props) {
this.props = props;
}
TextBox.prototype = {
builddom: function () {
// Building the text dom
console.log("TextBox", this.props);
}
}
function ImageBox(props) {
this.props = props;
}
ImageBox.prototype = {
builddom: function () {
// Building the text dom
console.log("ImageBox", this.props);
}
}
function Box (type, props) {
var handler = (type == 'text') ? TextBox :
(type == 'Image') ? ImageBox : null;
if (handler) {
handler.call(this, props);
Object.assign(this, handler.prototype);
}
}
var textbox = new Box ('text', {text: 'some'});
textbox.builddom ();
var imagebox = new Box ('Image', {x: 1, y: 2});
imagebox.builddom ();
It's not clear why you don't just use standard prototype inheritance here. It will allow you to both inherit or override methods of the parent. For example, ImageBox inherits the parent method and TextBox overrides:
/* Define Box */
function Box (type) {
this.type = type || 'box'
}
Box.prototype.builddom = function (){
console.log(this.type, ": build called")
}
/* Define TextBox */
function TextBox () {
Box.call(this, "text")
}
TextBox.prototype = Object.create(Box.prototype);
/* Override method */
TextBox.prototype.builddom = function (){
// call parent method too?
// Box.prototype.builddom.call(this)
console.log(this.type, "Text box override method")
}
/* Define ImageBox */
function ImageBox () {
Box.call(this, "image")
}
ImageBox.prototype = Object.create(Box.prototype);
var box = new Box ();
box.builddom();
var textbox = new TextBox ();
textbox.builddom();
var imageBox = new ImageBox ();
imageBox.builddom();
There is no need to create a box class if you are not going to use it, instead create a factory function and return a new instance of the respective class.
function AbstractBox() {}
AbstractBox.prototype.builddom = function() {
console.warn("unimplemented method");
};
function TextBox() {}
TextBox.prototype.builddom = function() {
console.log("TextBox.builddom called");
};
function ImageBox() {}
ImageBox.prototype.builddom = function() {
console.log("ImageBox.builddom called");
};
function ErrorBox() {}
function createBox(type) {
var handler = Object.create(({
"text": TextBox,
"Image": ImageBox
}[type] || ErrorBox).prototype);
handler.constructor.apply(handler, [].slice.call(arguments, 1));
for (var property in AbstractBox.prototype) {
var method = AbstractBox.prototype[property];
if (typeof method === "function" && !(property in handler)) handler[property] = method;
}
return handler;
}
(createBox("text")).builddom(); // Text
(createBox("Image")).builddom(); // Image
(createBox("error")).builddom(); // Error
My suggestion is to use composition/delegation rather than inheritance (has-a instead of is-a).
function TextBox () {
this.builddom = function () {
// Building the text dom
}
}
function ImageBox () {
this.builddom = function () {
// Building the image dom
}
}
function Box (type) {
var constructor =
(type == 'text') ? TextBox :
(type == 'Image') ? ImageBox : null;
var delegate = new constructor();
this.builddom = function () {
// Pre-work goes here.
delegate.builddom();
// Post-work goes here.
}
}
var textbox = new Box ('text');
textbox.builddom ();

call user function in foreach loop

i have understand that i need to change the global scope of this, because in the loop this refers to the window object. But if i try to define a variable in my foreach loop via a function its not working and i dont know why although my functio returns the correct value :(
// simple class for xml import
function io() {
this.vertexes = [];
this.getVertexByID = function(id) {
this.vertexes.forEach(function(entry) {
if (id == entry.id) {
// correct element found, displayed and returned
console.log(entry);
return entry;
}
});
}
this.importXML = function(xmlString) {
cells = this.xmlToJson(xmlString);
var parent = graph.getDefaultParent();
var _this = this;
graph.getModel().beginUpdate();
try {
// addEdges
cells.XMLInstance.Edges.Relation.forEach(function(entry) {
// both will be empty but i dont understand why :(
fromVertex = _this.getVertexByID(entry.fromNode);
toVertex = _this.getVertexByID(entry.toNode);
var e1 = graph.insertEdge(parent, null, '', fromVertex, toVertex);
});
} finally {
graph.getModel().endUpdate();
}
}
Returning a value in a forEach callback has no effect. It certainly is not the return value of the function that the forEach is part of.
So change this:
this.vertexes.forEach(function (entry) {
if(id==entry.id){
//correct element found,displayed and returned
console.log(entry);
return entry;
}
});
to this:
return this.vertexes.find(function (entry) {
return id==entry.id;
});

JavaScript: Accessing Nested Objects

The code looks like this
function Scripts() {this.FindById = function (id) {
this.FindById.constructor.prototype.value = function () {
return document.getElementById(id).value;
}}}
var Control = new Scripts();
Now when i say Control.FindById("T1").value(). I am not able to get the textInput("T1")'s value.
It seems that your code is a bit more complicated then it should be ;-)
Personally I would write it this way (not tested):
function Scripts() {
this.findById = function(id) {
var el = document.getElementById(id);
return {
value: function() {
return el.value;
}
}
}
}
The findById() now closes over a node and returns an interface that can return its value.
Also, your idea sounds a lot like Singleton, so you wouldn't even need the extra Scripts constructor:
var Control = {
findById: function(id) {
var el = document.getElementById(id);
return {
value: function() {
return el.value;
}
}
}
}
Working example: http://jsfiddle.net/YYkD7/
Try this:
function Scripts() {this.FindById = function (id) {
this.FindById.constructor.prototype.value = function () {
return document.getElementById(id).value
}}}
You didn't close the last "}" :-)

I have no idea how to test this with Qunit?

I want to test this function:
/js/lib/front.js
var Front = function(){
this.onSignUp = function(){
if (!Form.assertInput("email")) {
$("input[name=email]").focus();
this.showHiddenMessage("Email not set.");
return false;
}
}
}
I have in:
/js/lib/form.js
function Form() {
this.assertInput = function (name, defaultValue) {
var text = $("input[name=" + name + "]").val();
if (defaultValue != null) {
if (defaultValue && text == defaultValue)
return false;
}
if(this.trim(text)) return true;
return false;
}
}
This simple test passing:
test("Front", function() {
var front = new Front()
ok(front);
});
But if I write something like this:
test("On Sign Up ", function() {
var front = new Front()
equal(front.onSignUp(),false,"passing test");
});
I have error:
Died on test #1: Form.assertInput is not a function
I don't understand, what I need test in function like this and how include function inside another function?
I've saved a working fiddle here. As a side note, you might want to check out a tutorial on using qUnit, here.One thing that you need to pay attention to is when you're declaring your functions. It's saying Form.assertInput is not a function because you can't access it like that. You need to use the this keyword, which refers to current context. The code should be something like this:
var Form = function () {
//good to have assertInput first if you're using it in a later function
this.assertInput = function (name, defaultValue) {
var text = $("input[name=" + name + "]").val();
if (defaultValue != null) {
//safer to explicitly close your if statements with {}
if (defaultValue && text == defaultValue) {
return false;
}
}
if ($.trim(text)) { return true; }
return false;
};
this.showHiddenMessage = function (message) {
alert(message);
};
this.onSignUp = function() {
//this will point to the current context, in this case it will be Form class
if (!this.assertInput("email")) {
$("input[name=email]").focus();
this.showHiddenMessage("Email not set.");
return false;
}
};
};
Also in the example code that you gave you're missing the Front class. So I created a dummy one in my fiddle like this:
var Front = function() {};
Here are the tests that were run:
$(document).ready(function() {
test("Front", function() {
var front = new Front();
ok(front);
});
test("On Sign Up ", function() {
var form = new Form();
equal(form.onSignUp(), false, "passing test");
});
});

Categories