javascript Array in IE11 not processing the values correctly - javascript

I have a problem with a Javascript array in IE11 mainly in the for loop.
Here is the code:
function onResponseReceived(responseArray) {
found = true;
var i;
for(i in responseArray) {
var item = responseArray[i];
if (item.field_number == '5') {
item.value = intlToUsDate(item.value);
console.log(item.value);
}
var inputSelector = '[name="input_' + item.field_number + '"]';
var dom_elms = document.querySelectorAll(inputSelector);
for (var e in dom_elms) {
var dom_elm = dom_elms[e];
if (dom_elm.type == 'radio' || dom_elm.type == 'checkbox') {
if (dom_elm.value == item.value && !dom_elm.checked) {
dom_elm.click();
continue;
}
} else {
dom_elm.value = item.value;
}
}
}
}
Here is the output in IE11 using the console.log:
"
i
d
"
:
"
1
8
4
1
"
,
Here is an the output for the same Javascript using Chrome:
field_number
:
"5"
form_id
:
"10"
id
:
"1839"
is_synced
:
"1"
lead_id
:
"2967"
value
:
"05/08/2018"
__proto__
:
Object
Basically it process the information correctly.
In IE11, how can I have the array be an object like in Chrome,FF or Edge?
Thank you,
Kevin

querySelectorAll returns not an array but a NodeList, and its documentation says:
Don't be tempted to use for...in or for each...in to enumerate the
items in the list, since that will also enumerate the length and item
properties of the NodeList and cause errors if your script assumes it
only has to deal with element objects.
Therefore instead of below code
for (var e in dom_elms) {
var dom_elm = dom_elms[e];
:
}
you should use for IE:
Array.prototype.forEach.call(dom_elms, function(dom_elm) {
:
});

I found the solution. I had to add the following code:
var responseArray = JSON.parse(responseArray);

Related

jQuery to Vanilla JS - querySelector issues

I'm going through some code and working to change all of the jQuery to vanilla JS. However there is one section and I keep getting an error in my console that says either:
TypeError: document.querySelectorAll(...).toggle is not a function pr
TypeError: document.querySelectorAll(...) is null
Below is my code, the top part you can see is where I am trying to change the jquery to vanilla js (I have commented out the jquery) :
console.log(shipmentNumbers);
for (let i = 0; i < shipmentNumbers.length; i += 1) {
let sNumber = shipmentNumbers[i];
function getHistory(event) {
console.log(event);
document.querySelectorAll('#shipment' + sNumber + 'tr.show-history' + sNumber).toggle();
// $('#shipment' + sNumber + ' tr.show-history' + sNumber).toggle();
document.getElementsByClassName('overlay-line' + sNumber).style.display = 'table-row';
// $('.overlay-line' + sNumber).css({
// "display": "table-row"
// });
if (flag == false) {
let shipmentNumber = event.currentTarget.id.replace('status', '');
console.log('shipmentNumber=', shipmentNumber);
callHistoryApi(clientId, shipmentNumber);
$(this).find('.expand' + sNumber).html("▼");
flag = true;
} else {
$(this).find('.expand' + sNumber).html("►");
$('.overlay-line' + sNumber).css({
"display": "none"
});
flag = false;
}
}
Can someone explain why this isn't working, and how I can get it working using vanilla js?
I find that writing these two functions can really help when moving from jQuery to native JS.
function domEach(selector, handler, context) {
return Array.from(document.querySelectorAll(selector), handler, context);
}
// If you get a TypeError "Array.from" is not a function, use the polyfill
// found on MPN.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
This gets you around issues where you relied on implicit loops that jQuery uses.
// Instead of these:
document.querySelectorAll('#shipment' + sNumber + 'tr.show-history' + sNumber).toggle();
document.getElementsByClassName('overlay-line' + sNumber).style.display = 'table-row';
// Use these:
domEach('#shipment' + sNumber + 'tr.show-history' + sNumber, function (tr) {
tr.style.display = tr.style.display === "none"
? ""
: "none";
});
domEach('.overlay-line' + sNumber, function (el) {
el.style.display = 'table-row';
});
For a list of techniques to use instead of the jQuery functions, you can check You Might Not Need jQuery
Edit: more information about the code above
jQuery uses implicit loops. That is, when you do this:
$("#one").addClass("two");
jQuery does this behind the scenes:
var elements = document.querySelectorAll("#one");
var i = 0;
var il = elements.length;
while (i < il) {
elements[i].classList.add("two");
i += 1;
}
This leads to some confusion when going from jQuery to vanilla JavaScript since you have to manually loop over the results of querySelectorAll.
Array.from will loop over an array or array-like structure. querySelectorAll will return a NodeList - this is an array-like structure (it has numerical indicies and a length property). The domEach function allows us to pass a CSS selector to the function and will loop over the results of finding matching elements.
The ? : syntax is called a ternary operator. It's a short-cut for if ... else.
// Ternary operator
tr.style.display = tr.style.display === "none"
? ""
: "none";
// Equivalent if/else statements
if (tr.style.display === "none") {
tr.style.display = "";
} else {
tr.style.display = "none";
}
I hope that helps clarify things.
You must add check in whenever you do this, as .querySelctor/All() is going to return "null" if no elements are found.
var myCollection = document.querySelectorAll("selector");
if (myCollection.length > 0){
Array.prototype.forEach.call(myCollenction, function(element){
if(typeof element.toggle === "function"){
element.toggle();
}
})
}
More or less this will help you achieve your goal. However if you don't have method "toggle" defined on your elements - nothing will happen. :)

jQuery how to traverse json object dynamically

i validate my formular with ajax and get back the following json object:
{"username":["Please enter a username"],"email":["Please enter an email"],
"plainPassword":{"first": ["Please enter a password"]},"firstname":
["This value should not be blank."],"lastname":["This value should not be blank."],
"terms":["This value should be true."],"privacy":["This value should be true."],
"captcha":["Code does not match"],"securityQuestion":["This value should not be blank."],
"plainSecurityAnswer":["This value should not be blank."],"intention":
["This value should not be blank."],"addresses":[{"state":["This value should not be blank."],
"city":["This value should not be blank."],"zipcode":["This value should not be blank."],
"country":["This value should not be blank."]}]}
The keys are mapped to the input fields id always by:
var id = "fos_user_registration_form_" + key;
I want to present these errors in an efficient way as tooltips to the fields. For that, i've written the following jQuery code (where callback is the returned json object):
$.each( callback, function( key, entry ) {
if(key != "addresses" && key != "plainPassword")
{
var id = "#fos_user_registration_form_" + key;
$(id).tooltip('destroy');
$(id).tooltip({'title': entry});
$(id).closest('div[class="form-group"]').addClass('has-error');
}else if(key == "addresses"){
$.each( entry[0], function( keyAddress, entryAddress ) {
var id = "#fos_user_registration_form_" + key + "_0_" + keyAddress;
$(id).tooltip('destroy');
$(id).tooltip({'title': entryAddress});
$(id).closest('div[class="form-group"]').addClass('has-error');
});
}else if(key == "plainPassword")
{
var id= "#fos_user_registration_form_plainPassword_first,#fos_user_registration_form_plainPassword_second";
$(id).tooltip('destroy');
$(id).tooltip({'title': entry.first});
$(id).closest('div[class="form-group"]').addClass('has-error');
}});
It is working, but i think not very dynamic because i know in this case that the entries of the key "addresses" and "plainPassword" aren't strings and that i have to iterate on them again (here only on addresses).
Is there a nicer way to do this by only using the key and entry variable of the loops, without knowing the "key" names of the json ?
I thought of something like: While entry !== "string", iterate as long threwthe entries as there is another array or object in it and build up the "id" variable. When there is a string field as "entry", use it as tooltip text.
Hope you guys can help me.
Regards.
Recursion will do this!
eg http://repl.it/3hK/5
Code -
var id_stub = "#fos_user_registration_form_"
// Here's the recursive function - we kick it off below.
function process(thing, id) {
var key
for (key in thing) {
// Handle the arrays
if ('length' in thing[key]) {
// Handle the end - we found a string
if (typeof thing[key][0] == "string") {
var html_id = id_stub + id + key
var err_msg = thing[key][0]
console.log(html_id, ":", err_msg)
// Now do your jquery using the html_id and the err_msg...
}
// Else we found something else, so recurse.
else {
var i = 0;
while (i < thing[key].length) {
process(thing[key][i], key + "_" + i + "_")
i++
}
}
}
// Handle the objects by recursing.
else {
process(thing[key], key + "_")
}
}
}
// Start the recursion from here.
process(callback, "")
I added an extra address to test how this code handles nested addresses, and using that I get this in the console:
#fos_user_registration_form_username : Please enter a username
#fos_user_registration_form_email : Please enter an email
#fos_user_registration_form_plainPassword_first : Please enter a password
#fos_user_registration_form_firstname : This value should not be blank.
#fos_user_registration_form_lastname : This value should not be blank.
#fos_user_registration_form_terms : This value should be true.
#fos_user_registration_form_privacy : This value should be true.
#fos_user_registration_form_captcha : Code does not match
#fos_user_registration_form_securityQuestion : This value should not be blank.
#fos_user_registration_form_plainSecurityAnswer : This value should not be blank.
#fos_user_registration_form_intention : This value should not be blank.
#fos_user_registration_form_addresses_0_state : This value should not be blank.
#fos_user_registration_form_addresses_0_city : This value should not be blank.
#fos_user_registration_form_addresses_0_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_0_country : This value should not be blank.
#fos_user_registration_form_addresses_1_state : This value should not be blank.
#fos_user_registration_form_addresses_1_city : This value should not be blank.
#fos_user_registration_form_addresses_1_zipcode : This value should not be blank.
#fos_user_registration_form_addresses_1_country : This value should not be blank.
and sets up the variables you need to do your jQuery work.
function isValidationMessage(entry) {
return entry.length === 1 && typeof entry[0] === 'string';
}
function displayValidationMessage(key, message){
var id = "#fos_user_registration_form_" + key;
$(id).tooltip('destroy');
$(id).tooltip({'title': message});
$(id).closest('div[class="form-group"]').addClass('has-error');
}
function displayValidationMessageForArray(key, entries) {
for(var i = 0; i < entries.length; i++) {
$.each(entries[i], function(keyAddress, entryAddress) {
displayValidationMessage(key + "_i_" + keyAddress, entryAddress);
})
}
}
function displayValidationMessageForObject(key, entries) {
$.each(entries, function(entry, message) {
displayValidationMessage(key + "_" +entry, message);
})
}
function displayAllValidationMessages(callback) {
$.each( callback, function( key, entry ) {
if(isValidationMessage(entry)) {
displayValidationMessage(key, entry);
}else if($.isArray(entry)){
displayValidationMessageForArray(key, entry);
}else {
displayValidationMessageForObject(key, entry);
}
});
}
not fully tested, idea is to extract the if else to small function, and reuse them as much as possible

remove items from object if value is empty

So I'm taking a form and using serializeArray() to get all the forms data. There are 10 text inputs. What I am trying to do is skip the forms that return a empty result or forms that have "" as value. Here is what I came up with and it returns the index of the forms with "" or empty results correctly.
$("#" + forms).on("submit", function(event) {
var allData = $(this).serializeArray();
event.preventDefault();
for (var key in allData) {
if (allData[key].value === "") {
allData.splice(key, 1);
}
}
});
But when I add allData.splice(key, 1); it doesn't remove all the values with "" as a result. I basically want to remove any input that isn't going to have a value.
Also the structure of the objec is as follows.
[0]
name: "emailone",
value "some#email.com"
[1]
name: "passwordone",
value: "123asd"
[2]
name: "emailtwo",
value "another#email.com"
[3]
name: "passwordtwo",
value: "asd123"
[4]
name: "emailthree",
value ""
[5]
name: "passwordthree",
value: ""
That happens because you are altering the array while traversing it...
When you key is 4 and the value is '' you remove that element (succesfully) but when you splice the array it becomes a smaller one.. so the element at position 5 is now at 4. Your key variable is increased to 5 but now there is no element 5 in your array..
You need to traverse it backwards
$("#" + forms).on("submit", function(event) {
var allData = $(this).serializeArray();
event.preventDefault();
for (var key = allData.length-1; key >= 0 ; key--) {
if (allData[key].value === "") {
allData.splice(key, 1);
}
}
});
By splicing an array as you iterate over it, you can accidentally skip values - in this case, by removing a value at index four, you decrease the index of the following value by one. The loop than increments to five, and the value that started at the fifth index is skipped.
A few other answers have posted reasonable ways to work around this. If you're working with a newer version of JavaScript or with a library like jQuery or underscore, you could alternatively do this:
allData = allData.filter(function(e) {
return e.value !== "";
});
With jQuery, this can be written as
allData = $.grep(allData, function(e) {
return e.value !== "";
});
and in underscore:
allData = _.filter(allData, function(e) {
return e.value !== "";
});
var arrLength = allData.length, i, cleanArray = [], serializedArr;
for(i = 0; i < arrLength; i += 1{
if(allData[i] != ""){
cleanArray.push(allData[i]);
}
}
serializedArr = cleanArray.serializeArray();
You simply use delete to achieve your need,
for (var key in allData) {
if (allData[key].value === "") {
delete allData[key];
}
}
additionally you don't need explicitly get the value by this toallData[key].value, toallData[key] will simply return the value associated with the particular key.
DEMO
Updated:
for (var i=0; i < allData.length ; I++) {
if (allData[i].value === "") {
allData.splice(i, 1);
i--;
}
}
You should decrement the value of the index by one, every time you are removing an object from the array.

Find a value in an array of objects using Jquery

I have a array of objects say items. I want to find item having name xyz.
Currently, I wrote a for loop
for(var i = 0; i < items.length; i++)
{
if( items[i].name == "xyz" )
{
found = true;
break;
}
}
Is it possible to shorten this loop to one line statement using jquery? something like this:
$.find(items, "name", "xyz");
You might use
var matchingItems = items.filter(function(v){return v.name=="xyz"});
or using jQuery (for greater compatibility, as like many Array functions, filter or some aren't available on IE8) :
var matchingItems = $.grep(items, function(v){return v.name=="xyz"});
Something like:
$.grep(yourArray, function(n) {
return n.name == "xyz";
});
Use the native some method:
items.some(function(el) { return el.name == "xyz"; })
With jQuery, you would need to do something like $.grep and then check the length ($.grep(items, function(el){ return el.name=="xyz"; }).length > 1) - not an optimal solution. If you want to use a library solution for those browsers that don't support some (and don't want to use the polyfill), use Underscore's any.
var arr = ["abc", "xyz" ];
if($.inArray("abc",arr) > 0){
return true;
}
else
return false
​
this will return the index of "searching string"
if not found, it will return -1.
Use jQuerys inArray Example
http://api.jquery.com/jQuery.inArray/
might helps you
(function( $ ) {
$.find = function( array, property, key ) {
var found = false;
for(var i = 0; i < array.length; i++)
{
if( array[i][property] == key )
{
found = true;
break;
}
}
return found ? i : -1;
};
})( jQuery );
Usage: $.find(items, "name", "xyz");

javascript not removing undefined objects from array

I've got an in page text search using JS, which is here:
$.fn.eoTextSearch = function(pat) {
var out = []
var textNodes = function(n) {
if (!window['Node']) {
window.Node = new Object();
Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;
}
if (n.nodeType == Node.TEXT_NODE) {
var t = typeof pat == 'string' ?
n.nodeValue.indexOf(pat) != -1 :
pat.test(n.nodeValue);
if (t) {
out.push(n.parentNode)
}
}
else {
$.each(n.childNodes, function(a, b) {
textNodes(b)
})
}
}
this.each(function() {
textNodes(this)
})
return out
};
And I've got the ability to hide columns and rows in a table. When I submit a search and get the highlighted results, there would be in this case, the array length of the text nodes found would be 6, but there would only be 3 highlighted on the page. When you output the array to the console you get this:
So you get the 3 tags which I was expecting, but you see that the array is actually consisting of a [span,undefined,span,undefined,undefined,span]. Thus giving me the length of 6.
<span>
<span>
<span>
[span, undefined, span, undefined, undefined, span]
I don't know why it's not stripping out all of the undefined text nodes when I do the check for them. Here's what I've got for the function.
performTextSearch = function(currentObj){
if($.trim(currentObj.val()).length > 0){
var n = $("body").eoTextSearch($.trim(currentObj.val())),
recordTitle = "matches",
arrayRecheck = new Array(),
genericElemArray = new Array()
if(n.length == 1){
recordTitle = "match"
}
//check to see if we need to do a recount on the array length.
//if it's more than 0, then they're doing a compare and we need to strip out all of the text nodes that don't have a visible parent.
if($(".rows:checked").length > 0){
$.each(n,function(i,currElem){
if($(currElem).length != 0 && typeof currElem != 'undefined'){
if($(currElem).closest("tr").is(":visible") || $(currElem).is(":visible")){
//remove the element from the array
console.log(currElem)
arrayRecheck[i] = currElem
}
}
})
}
if(arrayRecheck.length > 0){
genericElemArray.push(arrayRecheck)
console.log(arrayRecheck)
}
else{
genericElemArray.push(n)
}
genericElemArray = genericElemArray[0]
$("#recordCount").text(genericElemArray.length + " " +recordTitle)
$(".searchResults").show()
for(var i = 0; i < genericElemArray.length; ++i){
void($(genericElemArray[i]).addClass("yellowBkgd").addClass("highLighted"))
}
}
else{
$(".highLighted").css("background","none")
}
}
If you look at the code below "//check to see if we need to do a recount on the array length. ", you'll see where I'm stripping out the text nodes based off of the display and whether or not the object is defined. I'm checking the length instead of undefined because the typeof == undefined wasn't working at all for some reason. Apparently, things are still slipping by though.
Any idea why I'm still getting undefined objects in the array?
My apologies for such a big post!
Thanks in advance
I've modified your eoTextSearch() function to remove dependencies on global variables in exchange for closures:
$.fn.extend({
// helper function
// recurses into a DOM object and calls a custom function for every descendant
eachDescendant: function (callback) {
for (var i=0, j=this.length; i<j; i++) {
callback.call(this[i]);
$.fn.eachDescendant.call(this[i].childNodes, callback);
}
return this;
},
// your text search function, revised
eoTextSearch: function () {
var text = document.createTextNode("test").textContent
? "textContent" : "innerText";
// the "matches" function uses an out param instead of a return value
var matches = function (pat, outArray) {
var isRe = typeof pat.test == "function";
return function() {
if (this.nodeType != 3) return; // ...text nodes only
if (isRe && pat.test(this[text]) || this[text].indexOf(pat) > -1) {
outArray.push(this.parentNode);
}
}
};
// this is the function that will *actually* become eoTextSearch()
return function (stringOrPattern) {
var result = $(); // start with an empty jQuery object
this.eachDescendant( matches(stringOrPattern, result) );
return result;
}
}() // <- instant calling is important here
});
And then you can do something like this:
$("body").eoTextSearch("foo").filter(function () {
return $(this).closest("tr").is(":visible");
});
To remove unwanted elements from the search result. No "recounting the array length" necessary. Or you use each() directly and decide within what to do.
I cannot entirely get my head around your code, but the most likely issue is that you are removing items from the array, but not shrinking the array afterwards. Simply removing items will return you "undefined", and will not collapse the array.
I would suggest that you do one of the following:
Copy the array to a new array, but only copying those items that are not undefined
Only use those array items that are not undefined.
I hope this is something of a help.
Found the answer in another post.
Remove empty elements from an array in Javascript
Ended up using the answer's second option and it worked alright.

Categories

Resources