Same code, same browser, different behavior for users - javascript

I have a piece of JS code which parses through a file, then associated the array with a key-value pair map, and then iterates it through it to find the proper city name with a .includes method. My problem is that the final field (where the function in question is called) works fine on my end for both Chrome and Firefox. It does not work for some reason for my group members.
This is the JS snippet that does the iterating:
Edit: this is how the file is being opened:
var rawFile = new XMLHttpRequest();
rawFile.open("GET", "../data/myFile.txt", false);
for (var i = 0; i < allText.length; i++) {
if (i % 2 == 0) {
myMap[allText[i]] = allText[i + 1];
}
}
var city = document.getElementById("city").value;
for (var key in myMap) {
if (myMap.hasOwnProperty(key)) {
if (myMap[key].toLowerCase().includes(city.toLowerCase())) {
document.getElementById("city").value = myMap[key]
document.getElementById("zipcode").value = key;
}
}
}
This is the html part that calls it:
<label for="myLabel">City: </label>
<input type="text" name="myLabel" id="myLabel" onblur="readTextFile()">
What exactly is the problem and how can I troubleshoot it as it makes no sense to me, coming from the world of Java and C++, where I have never faced such an issue before. If you are wondering why the JS might be kinda ugly, it is the result of a student with a teacher who thinks that showing W3Schools examples is equivalent to good teaching.

Javascript includes function may work erratically due to some browsers not supporting it. You need to be wise while choosing the javascript functions specially when mozilla may have some functions which are not supported on some browsers. W3schools also provides a list of Browser support for a function. Check the link below for that list for includes function:
http://www.w3schools.com/jsref/jsref_includes.asp
Alternatively, you can use indexOf function like:
myMap[key].toLowerCase().indexOf(city.toLowerCase()) >= 0
I have myself faced issues with includes function hence providing you a workaround. Happy programming.

Related

I get a syntax error in IE but not in Chrome

So I have this code (a function) that works in Google Chrome/Fire Fox but not in IE. If I comment this certain line, everything runs fine, except that line is crucial.
I have this function called ReadCookie, which basically just stores the cookies into an array called cookiearray.
function ReadCookie() {
var allcookies = document.cookie; //variable called "allcookies" stores all the cookies.
cookiearray = allcookies.split(';').map(c => c.split('=')[1]); //cookiearray is an array that has all the values as strings.
}
IE said that the 4th line is incorrect cookiearray = allcookies.split(';').map(c => c.split('=')[1]); but I don't know why.
Thanks!
Arrow functions (like c => c.split('=')[1]) are a new feature in ES6. Chrome supports them. Internet Explorer does not.
I believe it's an ECMA script 6 thing with the way you're using the map.
So you can write it like this instead:
cookiearray = allcookies.split(';').map(function (c) {
return c.split('=')[1];
}); //cookiearray is an array that has all the values as strings.
The solution I implemented was as follows
goto: https://babeljs.io/repl
Paste in your code and select es2015.
In your new code paste the following, if you are using forEach (which is again not supported in IE) :
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
Use indexOf instead of includes

Trying to make sense of "this" in my javascript code (one thing works, the other doesn't)

I've been trying to learn javascript by refactoring some Jquery examples in a book into javascript. In the following code I add a click listener to a tab and make it change to active when the user clicks on the tab.
var tabs = document.querySelectorAll(".tabs a span");
var content = document.querySelectorAll("main .content li");
for (var tabNumber = 0; tabNumber <= 2; tabNumber++) {
tabs[tabNumber].addEventListener("click", function (event) {
for (var i = 0; i < tabs.length; i++) {
tabs[i].classList.remove("active");
}
tabs[tabNumber].classList.add("active");
for (var i = 0; i < content.length; i++) {
content[i].innerHTML = "";
}
event.preventDefault();
});
}
This returns an undefined error when I run it. However, I tried replacing tabs[tabNumber].classList.add("active") with this.classList.add("active") and it worked.
Why doesn't the previous code work? As far as I can see they are referring to the same thing, and tabs[tabNumber] should work since at that point in the code it is tabs[0].
If use this, I think it's better and a more polished solution. If you still want to use tabNumber, it's probably evaluating to 3 in every click callback, because it's the number after the last iteration, and you don't have a tabs[3] position.
So, you just have to make a closure of the tabNumber variable.
I guess other answers told you why tabs[tabNumber] does not work (because it comes from the score of the for loop and so, is always equal to the greater value of tabNumber).
That's why I would recommend using a .forEach loop. Careful though because it doesn't work on arrays of DOM nodes produced by document.querySelectorAll(), but you can use:
// ES6
Array.from(document.querySelectorAll('...'))
// ES5
[].slice.call(document.querySelectorAll('...'))
Anyway, I made a simplified working demo of your code.
Note that I save the currently active tab in a variable, to prevent another for loop. You could also do:
document.querySelector('.active').classList.remove('active')
But I like to reduce the amount of DOM reading.
Good luck for your apprentissage, re-writing some jQuery code into Vanilla JS seems like a good method, and you might acquire a far better comprehension of JavaScript.

iBooks JavaScript Environment - invalid element name

So, I've narrowed down my error (well, at least the first one) to this function:
var genArray = function () {
var arr, len, i;
if(arguments.length > 0) {
len = [].slice.call(arguments, 0, 1)[0];
arr = new Array(len);
for(i = 0; i < len; i++) {
arr[i] = genArray.apply(null, [].slice.call(arguments, 1));
}
} else {
return null; //or whatever you want to initialize values to.
}
return arr;
}
Then, I get a very unhelpful error:
error on line 71 at column 23: StartTag: invalid element name
Below is a rendering of the page up to the first error
Now, the function is decidedly not on line 71 (perhaps it is in the compiled ePub, but I have no idea how they correlate). Further, I have no idea what that error means in a JavaScript context. Also, this code works fine in a browser (Safari included).
Any ideas what could be causing the issue?
EDIT: On a whim, I checked whether [] was the problem by changing it to Array(). No luck.
Okay, so I discovered a solution to my problem. I just needed to surround my JavaScript in CDATA tags like so:
//<![CDATA[
var genArray = function () {
var arr, len, i;
if(arguments.length > 0) {
len = [].slice.call(arguments, 0, 1)[0];
arr = new Array(len);
for(i = 0; i < len; i++) {
arr[i] = genArray.apply(null, [].slice.call(arguments, 1));
}
} else {
return null; //or whatever you want to initialize values to.
}
return arr;
}
//]]>
I discovered this by using the epubcheck tool which said something to the effect that the file must have properly formed characters or something. I don't recall the exact message. Anyways, this reminded me of a problem I had in a script where I used some unicode characters. I remembered about CDATA which solved it. Then I found this stackoverflow question which basically says it's necessary for when your pages must be interpreted as XML/XHTML as well, which is the case for ePubs.
So, moral of the story is wrap javascript in CDATA tags for ePubs or iBooks.
EDIT: It should be noted that it's worth doing this around all of your JavaScript. The issue in my case was the < less than operator being interpreted as the start of a tag. However, it is probably cleaner to just include the CDATA tag around all of your JavaScript rather than trying to isolate sources of the issue.
EDIT 2: In the interest of aggregating information to whoever finds this answer useful, it should also be noted that having all of one's JavaScript in external files probably also works (according to the source linked in the answer to the question I've linked to). I don't care to test this at the moment, but it should work because the external JavaScript will not be parsed as XML like it is inside of a <script> tag.
The error you report indicates the XHTML file source is in error. I would take a look at, uhh, line 71 column 23 of the XHTML file in question. What's there? Could it possibly be <StartTag>? Is the XHTML being generated programatically somehow? EPUBs are not "compiled"; they are just zipped, and this line/column information refers to the actual position in the XHTML file in the EPUB. What does epubcheck say?
This kind of error message would not be generated by problems in any dynamic HTML created via script; those would result in a DOMError.
My guess is that iBooks is finding some error in the function at parse time, which terminates the parsing process before the XHTML parsing is completed and the XHTML error can be reported. However, I can't imagine what the error might be; I doubt it's the missing semi-colon at the end of the function, but could possibly be depending on what's on the next line.
Totally minor point, but
len = [].slice.call(arguments, 0, 1)[0];
is the same as
len = arguments[0];
Sounds to me more like an XHTML error. When you run in the browser if you are not opening it as an XHTML file, do so and see if it breaks. Browsers tend to be more lenient than EPUB readers. You are most likely creating some sort of invalid HTML element with your slices, it would be great to have the full page to identify exactly what 'getArray()' is returning...

Javascript: TypeError variable is undefined

I am currently building a small web application with similar functionality across all modules. I want to code small generic functions so that all programmers next to me, call these functions and these functions return necessary but important data for them to implement their functionality. In this example, I am trying to deal with the typical "choose true or false" exercise. So from the template.php they call this function:
function checkAnswers(){
var radiobuttons = document.form1.exer1;
var correctAnswers = answers(); //this is an array of string
var checkedAnswers = checkExerciseRB(radiobuttons, 2, correctAnswers);
for(i=0; i<checkedAnswers.length; i++){
alert(checkedAnswers[i]);
}
}
Function checkExerciseRB is my generic function, it is called from checkAnswers.
function checkExerciseRB(rbuttons, opciones, correct){
var answers = new Array();
var control = 0;
for(i=0; i<rbuttons.length; i++){
var noPick="true";
for(j=0; j<opciones; j++){
if(rbuttons[control+j].checked){
if(rbuttons[control+j].value==correct[i]){
answers[i]= 1;
noPick="false";
break;
}
else{
answers[i]=2;
noPick="false";
break;
}
}
}
if(noPick=="true")
answers[i]=0;
control=control+opciones;
}
return answers;
}
It works great but while looking at my favorite browsers (FireFox, Chrome) error log it says:
TypeError: rbuttons[control + j] is undefined
Any clue on how to deal with this matter?
This probably means that control + j is greater than or equal to the length of the array rbuttons. There's no such array element as rbuttons[control + j].
You should learn how to use the JavaScript debugger in your favorite browsers! Debuggers are great. They let you watch this code run, line by line, as fast or as slow as you want, and watch how the value of control changes as you go.
You’ll watch it, and you’ll think “Oh! That line of code is wrong!”
You're looping through rbuttons.length times, but in each loop you're adding 2 to control. Using control to index your array, you're going to run past the end.
Does the index specified by control + j exist in the array? i.e: If that evaluates to 4, is there at least 5 items in the array?
Also, you should be using var i, var j, etc inside your for loop. Without it your variables are leaking into the scope this code is executed in (most likely the global scope, and that's not good) :)

How To Join Relative URLs in JavaScript

I want to join two strings each representing a relative URL in Javascript.
I want to join the base URL http://www.adress.com/more/evenmore with the following examples:
../../adress (with the expected output: http://www.adress.com/adress)
../adress (with the expected output http://www.adress.com/more/adress)
What would be the best way? I was thinking of using regexp and checking
how many ../ preceed the relative URL, then subtracting that amount from the baseurl and adding them to what is left.
8 years later, many browsers (except for Internet Explorer) support the URL constructor (URL(url [, base])).
> new URL('../address', 'http://www.adress.com/more/evenmore/').href
"http://www.adress.com/more/address"
> new URL('../../address', 'http://www.adress.com/more/evenmore/').href
"http://www.adress.com/address"
The following function decomposes the URL then resolves it.
function concatAndResolveUrl(url, concat) {
var url1 = url.split('/');
var url2 = concat.split('/');
var url3 = [ ];
for (var i = 0, l = url1.length; i < l; i ++) {
if (url1[i] == '..') {
url3.pop();
} else if (url1[i] == '.') {
continue;
} else {
url3.push(url1[i]);
}
}
for (var i = 0, l = url2.length; i < l; i ++) {
if (url2[i] == '..') {
url3.pop();
} else if (url2[i] == '.') {
continue;
} else {
url3.push(url2[i]);
}
}
return url3.join('/');
}
Using URI.js (urijs - npm): absoluteTo():
function joinUrl(baseUrl, url) {
var theUrl = new URI(url);
if (theUrl.is("relative")) {
theUrl = theUrl.absoluteTo(baseUrl);
}
return theUrl.toString();
}
The ECMAScript URL Web API mentioned by #ning is a good place to start: especially as it is available in vanilla JS implementations (Node, etc.) and doesn't require you to use a library that does something the implementation nowq already accomplishes. Consulting the MDN documentation, more specifically the examples, is a great place to start.
Borrowing (somewhat) directly from their documentation:
let m = 'https://developer.mozilla.org';
// ... omitted
let d = new URL('/en-US/docs', m);
If you were to do the above, then console.log the .toString of the constructed URL object, your output would be: 'https://developer.mozilla.org/en-US/docs'.
Importantly, if you consult the Syntax section of the documentation, you will note that the second argument is optional and the second argument (as seen in the above example) represents the base URL (though only in the case of two arguments being supplied).
If both argument values are absolute URLs, Web API honors the first and discards the second.
If you are working with Node.js, I would encourage you to look at the native path module for doing work on relative paths, again over using a library. The main motivation here is that spinning up your own algorithm (probably just a call to path here and there) is potentially better than introducing a library that will pull in several other dependencies that may introduce vulnerabilities and unnecessary bloat to your application (or just be too heavy weight for what you need).
However, if you are working on the front end, you won't have path available to you and - as mentioned in #cburgmer's answer comments - Web API's URL doesn't support the relative path case mentioned. In this case, you may need to look for a library to accomplish this for you; however, again, given the other answers, I'd consider trying out a home-spun approach.
To their credit, URI.js currently only integrates one non-dev. dependency and doesn't have a huge footprint.

Categories

Resources