Continue executing a dom command - javascript

I use this dom command in some testing pages:
document.querySelector('div#summary-item div.description').innerHTML || ""
But in some pages when the first part does not exist I don't receive the second "" but I receive this error and my programm stops
Uncaught TypeError: Cannot read property 'innerHTML' of null(…)
Is there any simple way to receive the "" without the need to use the typeof in an if statement?

You don't need typeof but you do need an if statement or some other flow control.
var el = document.querySelector('div#summary-item div.description');
var data = "";
if (el)
data = el.innerHTML;
Or here it is using the conditional operator:
var el = document.querySelector('div#summary-item div.description');
var data = el ? el.innerHTML : "";
Technically, you could get away without using a flow control statement or expression by using an object that has a .innerHTML property.
var data = (document.querySelector('div#summary-item div.description') || {innerHTML:""}).innerHTML;
But I think that's ugly. if statements are part of the language. Not sure why you'd want to avoid it.
Of course, you could always use a function that abstracts it away if it really bothers you.
function htmlFromElem(selector) {
var el = document.querySelector(selector);
return el ? el.innerHTML : "";
}
Then use it like this:
var data = htmlFromElem('div#summary-item div.description')

Related

Umbraco v8: custom script call model change

I have an Umbraco plugin that is literally a vanilla .js file that allows me to retrieve some JSON from a service, then it fills in some of the fields on the current page, e.g by doing:
var setTxt = function(id, val) {
var txt = document.getElementById(id);
if (txt && val != null && val != undefined) {
return txt.value = val;
};
};
However, when I hit save, the angular model doesn't save any of the changes to the inputs - probably because no change detection has been triggered.
I've tried e.g with the field 'publisher':
angular.element(document.querySelector("#publisher")).scope().apply()
but I get the error:
VM94421:95 TypeError: Cannot read property 'apply' of undefined
I really don't want to get stuck into angular 1, my vanilla js is all working, I just need to get umbraco to scoop up all the values that I've set on the various fields.
Q) How can I force this?
p.s. - please don't comment that this is bad practice, I just need to get this job done.
EDIT by question poster:
Turns out as jQuery Lite is included already, you can just call:
$('#' + id).trigger('input');
Original answer below also works:
You should trigger "input" event to let angular know your changes:
var setTxt = function(id, val) {
var txt = document.getElementById(id);
if (txt && val != null && val != undefined) {
txt.value = val;
raiseEvent(txt, 'input');
return val;
};
};
var raiseEvent = function(el, eventType) {
var event = document.createEvent('Event');
event.initEvent(eventType, true, true);
el.dispatchEvent(event);
};
BTW, $scope doesn't have "apply" function. Its name is "$apply". Also as far I understand "scope.$appy(cb)" will pick up changes that are applied to the scope variable, in your case you manipulate directly with dom element.
Debug Data must be enabled enabled, in order for functionality to call angular.element(yourElement).scope() to work. It looks like its enabled by default.

Variable cannot be changed inside Window object

I'm developing a cordova app and within it, I'm using an iFrame. I'm using the following code to communicate between the iFrame and the app itself:
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
var message = "";
var output = "";
eventer(messageEvent, function(e) {
message = e.data;
var data = JSON.parse(message);
for (var i = 0; i < data.messages.length; i++){
var msg = data.messages[i];
output = msg.msg;
alert(output); //*1
}
}, false);
I'm copying the code from here: https://davidwalsh.name/window-iframe
The alert inside the eventer at *1 show the output variable is what I want. But once outside of this eventer function, the variable output reverts to blank.
After some research I think it might because I'm creating a Window object, but I'm not sure if that's the case or exactly what it is.
How can I permenently modify the variable "output"? Thanks.
Try changing the for loop to this:
for (var i = 0; i < data.messages.length; i++){
var dataMsg = data.messages[i].msg;
output = dataMsg; alert(output); //*1
}
I don't know if there is an issue with assigning msg.msg to output.
The problem is with your understanding of how events work.
Judging from your comments, this is how you think it works:
// Ordering
var output = ""; // 1. Assign empty string to output
eventer(messageEvent, function(e) {
// assign value to output // 2. Assign value to output
});
alert(output); // 3. Print output
In reality, it works like this:
var output = ""; // 1. Assign empty string to output
eventer(messageEvent, function(e) {
// assign value to output // 3. Assign value to output
});
alert(output); // 2. Print output
The value is changed by your callback function, but you are accessing it in the wrong order. eventer, or in modern browsers, .addEventListener, registers a callback function. Your callback function will only get triggered after the current thread is finished. Of course it will print out an empty value, since alert is executed before you even changed its value.
If you are not familiar with the basics of event-based programming, I suggest you to take a look at this tutorial.
Just a side note, never use an iframe unless you have specific reasons of doing so. And also you do not need the first few lines since you are using Cordova. attachEvent is IE-specific.
As a general advice, try not to pollute the global scope with unnecessary variables in the future.

Avoiding multiple load of javascript file

I add this snippet to each javascript file used in my asp.net web api application to avoid multiple load :
Fullcalendar.js
blog = {};
blog.comments = blog.comments || {};
blog.comments.debugMode = false;
blog.isFirstLoad = function (namesp, jsFile) {
var isFirst = namesp.jsFile.firstLoad === undefined;
namesp.jsFile.firstLoad = false;
return isFirst;
};
$(document).ready(function () {
if (!blog.isFirstLoad(blog.comments, "fullcalendar.js")) {
return;
}
});
Sometimes I get a weird exception
Uncaught TypeError: Cannot read property 'firstLoad' of undefined
I need to know :
Why this happens?
How can I fix it?
A couple of problems there.
First, you shouldn't be loading the file more than once in the first place, so it shouldn't be necessary to go through this business of trying to figure out whether you've loaded it.
But if you want to do that:
The first practical issue is that you're always doing this:
blog = {};
...which means if there's already a blog global, you're wiping out its value and replacing it with an empty object. If you want to use an existing global's value or create a new one, do this:
var blog = blog || {};
That seems odd, but since repeated var declarations are fine (and don't change the variable), that will use an existing one's value, or if there isn't one (or its value is falsey) it will create a new one and initialize it with {}.
Then, the line
namesp.jsFile.firstLoad = false;
...looks for a property called jsFile on namesp and assumes it's not null or undefined. It doesn't look for a property using the jsFile argument's value.
To do that, use brackets notation:
namesp[jsFile].firstLoad = false;
Even then, though, you're assuming it's not null or undefined, but it may well be. You probably just wanted:
namesp[jsFile] = false;
Or possibly:
namesp[jsFile] = namesp[jsFile] ||{};
namesp[jsFile].firstLoad = false;
That said, it seems really odd to use blog.comments to track whether JavaScript files have been loaded. If the file may have already been loaded, just this will do it:
var fullCalendarLoaded;
if (fullCalendarLoaded) {
// It's already loaded
} else {
// It isn't, but it is now
fullCalendarLoaded = true;
// ...do your init...
}
Or if you have several of these and want to use a single global for it:
var loadedScripts = loadedScripts || {};
if (loadedScripts.fullCalendar) {
// Already loaded
} else {
// Not loaded yet
loadedScripts.fullCalendar = true;
// ...do init...
}
Or if using the filename is important:
var loadedScripts = loadedScripts || {};
function firstLoad(filename) {
if (loadedScripts[filename[) {
return false;
}
// Not loaded yet, remember we've loaded it now
loadedScripts[filename] = true;
return true;
}
Then:
if (firstLoad("fullcalendar.js")) {
// First load, do init...
}
It's fairly straightforward:
On your initial run, you define
blog = {};
blog.comments = blog.comments || {};
blog.comments.debugMode = false;
In theory, this means that on some loads, blog is:
var blog = {
comments: {
debugMode: false
}
}
You then pass blog.comments into your function isFirstLoad as the namesp parameter. In that function, you do the evaluation:
namesp.jsFile.firstLoad === undefined;
Well, you never defined the jsFile property of blog.comments. This means it is undefined. Trying to access the property firstLoad of an undefined variable will give you your error
Uncaught TypeError: Cannot read property 'firstLoad' of undefined

Function only partially executing

I guess I'm a beginner at javascript and as the title suggests, my function won't fully execute.
function showBorder2(classid){
var first2 = document.getElementById("swordsmanIcon");
var second2 = document.getElementById("marksmanIcon");
var third2 = document.getElementById("scribeIcon");
first2.style.borderColor = "transparent";
second2.style.borderColor = "transparent";
third2.style.borderColor = "transparent";
classid.style.borderColor = "Aqua";
alert("1");
var classType = elementid.id;
alert("2");
window.classType = classType;
}
I've ran a little test and i have discovered that alert 1 triggers, but alert 2 does not... This is probably a simple mistake but I really can't see it.
Thanks in advance!
if alert1 is triggering but not alert2, the problem must be that elementid is not defined and trying to access a property of an undefined variable will throw an error and break execution of your js. The solution:
a) make sure to define elementid
b) if you can't guarantee definition of elementid, add a check to see if it is defined before trying to access a child property.
A simple shorthand for this is var classType = elementid && elementid.id;
^ that's short for something like this:
var classType;
if(elementid) {
classType = elementid.id;
}
Probably one of your element variable (first2, second2, third2, classid) is either null or undefined. It is good defensive programming (especially in javascript) to check for null or undefined before using the variable.
I'm guessing when the showBorder2 function is called, the parameter passes to "classid" either is not a valid element or nothing is pass to it.

Cache is not defined error

function example(str) {
var cache = ( str != "" ) ? str : null;
}
example("something");
alert(cache); // cache is not defined
On alert, it says cache is not defined. How to make it so that after calling a function, cache will be saved and I could invoke it like alert(cache).
The variable 'cache' is defined in the function example and not outside that scope so alert does not have access to it. Please look at other similar questions, example: https://stackoverflow.com/questions/22113894/variables-from-anonymous-function/22114051#22114051 ; not 10 minutes ago. I would also recommend reading on Javascript especially how variable scope works. It is very similar to most programming languages but funky in a couple other, ex: http://msdn.microsoft.com/en-us/library/bzt2dkta(v=vs.94).aspx
Not recommended, but a quick answer is:
var cache;
function example(str) {
cache = ( str != "" ) ? str : null;
}
example("something");
alert(cache);
cache is a local variable to example function. I suggest to use a namespace instead of a global variable. If it wont be a global variable, be free to use a classic var declaration.
so:
var app = app || {}; //define namespace
app.cache = "";
function example(str) {
app.cache = str != "" ? str : null;
//i guess it should equal to:
// cache = str ? str : null;
}
console.log(str); //similar to alert, but logs in developers tool (chrome) or firebug(FFx)
ps: I suggest the use of console.log() (or debug) instead of alert(). It's more comfortable than alert()
Self-memorizing functions
Memorization is the process of building a function that’s capable of
remembering its previously computed values. This can markedly increase
performance by avoiding needless complex computations that have
already been performed.
function foo(str) {
if (!foo.cache) {
foo.cache = ( typeof str !== undefined ) ? str : null;
}
}
foo("something");
alert(foo.cache);
Define cache outside function exmaple()
var cache;
function example(str) {
cache = ( str != "" ) ? str : null;
}
When you define inside the function it's scope would be finished inside it, so you can not access cache from outside of function in your case.

Categories

Resources