textContent Vs. innerText Cross Browser solution - javascript

I've been having a hard time with cross browser compatibility and scrapping the dom.
I've added data analytics tracking to ecommerce transactions in order to grab the product and transaction amount for each purchase.
Initially I was using document.querySelectorAll('#someId')[0].textContent to get the product name and that was working fine for every browser except internet explorer.
It took some time to figure out that it was the .textContent part that was causing ie problems.
Yesterday I changed .textContent to .innerText. From looking inside analytics it seems that the issue has been resolved for ie but now Firefox is failing.
I was hoping to find a solution without writing an if statement to check for the functionality of .textContent or .innerText.
Is there a cross browser solution .getTheText?
If not what would be the best way around this? Is there a simple solution? (I ask given my knowledge and experience with scripting, which is limited)
** added following comments **
If this is my code block:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].innerText;
prd.name = name[i].innerText;
prd.price = price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Then if I understand the syntax from the comments and the question linked to in the comment, is this what I should do:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
prd.brand = brand[i].textContent || brand[i].innerText;
prd.name = name[i].textContent || name[i].innerText;
prd.price = price[i].textContent || price[i].innerText;
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
So using or with a double bar || assigns the first non null value?

Re: your edit, not quite. The way to access methods or properties on an object (eg a DOM element) is to use dot notation if you have the name itself, or square brackets in case of variables/expressions (also works with strings, as in obj["propName"], which is equivalent to obj.propName). You can also just test the property against one element and use that from there on:
// build products object
var prods = [];
var brand = document.querySelectorAll('.txtStayRoomLocation');
var name = document.querySelectorAll('.txtStayRoomDescription');
var price = document.querySelectorAll('.txtStayRoomSplashPriceAmount');
for(var i = 0; i < brand.length; i++) {
//set granular vars
var prd = {};
//add to prd object
var txtProp = ("innerText" in brand[i]) ? "innerText" : "textContent"; //added string quotes as per comments
prd.brand = brand[i][txtProp];
prd.name = name[i][txtProp];
prd.price = price[i][txtProp];
prd.quantity = window.session_context_vars.BookingContext.Booking.ReservationLineItems[i].ReservationCharges.length/2;;
//add to prods array
prods.push(prd);
}
Regarding the line:
var txtProp = (innerText in brand[i]) ? innerText : textContent;
The in keyword checks an object to access the property (syntax: var property in object). As for the question notation (I made an error earlier, using ||, the correct thing to use was a :),
var myVar = (prop in object) ? object[prop] : false;
As an expression, it basically evaluates the stuff before the ?, and if it's true, returns the expression before the :, else the one after. So the above is the same as / a shorthand for:
if(prop in object){
var myVar = object[prop];
}
else{
var myVar = false;
}
Since you are checking between two properties only and wanting to assign one or the other, the shortest way would indeed be:
var txtProp = brand[i].innerText || brand[i].textContent;
It would basically test the first property, and if it were false or undefined, it would use the second one. The only reason I (pedantically) avoid using this is because the first test of a || b would fail even if a existed but just had a value of 0, or an empty string (""), or was set to null.

Related

Get and set object values in localStorage array

I'm trying to set objects into localStorage with a format similar to the following:
[{"1":{"property1":false,"property2":false}},{"2":{"property1":false,"property2":false}}]
Where I'd be able to set the 1 or 2 based on a dynamic value I'm getting from a REST call. What I have so far is:
// check if session exists and create if not
var StorageObject = JSON.parse(localStorage.getItem("session")) || [];
//see if the current id from the REST call is in storage and push with properties if not
if ( !StorageObject[thisItemsListID] ) {
var itemProperties = {};
itemProperties[thisItemsListID] = {};
itemProperties[thisItemsListID]["property1"] = false;
itemProperties[thisItemsListID]["property2"] = false;
StorageObject.push(itemProperties);
localStorage.setItem('session', JSON.stringify(StorageObject));
}
I can get the data into localStorage using this format but StorageObject[thisItemsListID] always gets into the if statement and generates a duplicate item in localStorage and I'm not sure how to access this with a variable. I'm trying to append the new ID if it doesn't exist so if {1:{} exists but current ID is 2 I need to push the new value.
I'm close here and maybe I need to reevaluate the format I'm storing the data string but I'm going in circles here and could use a point in the right direction.
Well, the duplicate item is happening in StorageObject.push(itemProperties).
Try this to update the object:
//StorageObject.push(itemProperties); <-- remove
StorageObject[thisItemsListID] = itemProperties;
[EDIT]
If you want to keep [{"1":{"property1":false,"property2":false}},{"2":{"property1":false,"property2":false}}]. To conditional would be a bit different.
var haveItem = StorageObject.filter(function(item){
return Objects.keys(item)[0] == thisItemsListID;
}).length > 0;
if ( !haveItem ) {
var itemProperties = {};
itemProperties[thisItemsListID] = {};
itemProperties[thisItemsListID]["property1"] = false;
itemProperties[thisItemsListID]["property2"] = false;
StorageObject.push(itemProperties);
localStorage.setItem('session', JSON.stringify(StorageObject));
}
Are you trying to update the object or just overwrite it? Filipes response illustrates how to update the entire storage object by just reassigning the object with the new value.
If you wanted to update just as section/ value of the object you could do so using a for loop. This would allow you to scan the array locate the one property and then remove it, updated it, overwrite it etc.
Here is an example of the loop. Bear in mind This is a snippet from a report library I was building. It uses angular $scope but it is a complex type doing a similar action to your update (here I am setting a label as a favorite/bookmark)
function OnFavoriteComplete(response) {
var id = response.config.data.reportId; //dynamic values set by client
var isFavorite = response.config.data.isFavorite;//dynamic values set by client
var arrayCount = $scope.reportList.length;
//loop my current collection and look for the property id of the label
//then check to see if true or false/this was a toggle enable disable
if (isFavorite) {
for (var i = 0, iLen = arrayCount; i < iLen; i++) {
if ($scope.reportList[i].reportId == id) {
$scope.reportList[i].isFavorite = false;
}
}
}
//if false update the property with the new value
else {
for (var i = 0, iLen = arrayCount; i < iLen; i++) {
if ($scope.reportList[i].reportId == id) {
$scope.reportList[i].isFavorite = true;
}
}
}
};
If you are using another framework like lowDash it has some really nice helper functions for updating and evaluating arrays.

Google Script to see if text contains a value

I have a google form that when the user submits it will trigger my function to run which is creating a summary of what they submitted as a Google Doc. I know it can automatically send an email but I need it formatted in a way that my user can edit it later.
There are some check boxes on the form -- but the getResponse() is only populated with the items checked and I need it to show all possible choices. Then I will indicate somehow what was checked.
I can't find a way to see if a text contains a value.
Like in Java with a String, I could do either .contains("9th") or .indexOf("9th") >=0 and then I would know that the String contains 9th. How can I do this with google scripts? Looked all through documentation and I feel like it must be the easiest thing ever.
var grade = itemResponse.getResponse();
Need to see if grade contains 9th.
Google Apps Script is javascript, you can use all the string methods...
var grade = itemResponse.getResponse();
if(grade.indexOf("9th")>-1){do something }
You can find doc on many sites, this one for example.
Update 2020:
You can now use Modern ECMAScript syntax thanks to V8 Runtime.
You can use includes():
var grade = itemResponse.getResponse();
if(grade.includes("9th")){do something}
I had to add a .toString to the item in the values array. Without it, it would only match if the entire cell body matched the searchTerm.
function foo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = ss.getSheetByName('spreadsheet-name');
var r = s.getRange('A:A');
var v = r.getValues();
var searchTerm = 'needle';
for(var i=v.length-1;i>=0;i--) {
if(v[0,i].toString().indexOf(searchTerm) > -1) {
// do something
}
}
};
I used the Google Apps Script method indexOf() and its results were wrong. So I wrote the small function Myindexof(), instead of indexOf:
function Myindexof(s,text)
{
var lengths = s.length;
var lengtht = text.length;
for (var i = 0;i < lengths - lengtht + 1;i++)
{
if (s.substring(i,lengtht + i) == text)
return i;
}
return -1;
}
var s = 'Hello!';
var text = 'llo';
if (Myindexof(s,text) > -1)
Logger.log('yes');
else
Logger.log('no');

How to split a URL string with parameters into an array using JavaScript

I'm trying to break up a string like this one:
fname=bill&mname=&lname=jones&addr1=This%20House&...
I want to end up with an array indexed like this
myarray[0][0] = fname
myarray[0][1] = bill
myarray[1][0] = mname
myarray[1][1] =
myarray[2][0] = lname
myarray[2][1] = jones
myarray[3][0] = addr
myarray[3][1] = This House
The url is quite a bit longer than the example. This is what I've tried:
var
fArray = [],
nv = [],
myarray = [];
fArray = fields.split('&');
// split it into fArray[i]['name']="value"
for (i=0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push(nv[0],nv[1]);
nv.length = 0;
}
The final product is intended to be in 'myarray' and it is, except that I'm getting a one dimensional array instead of a 2 dimensional one.
The next process is intended to search for (for example) 'lname' and returning the index of it, so that if it returned '3' I can then access the actual last name with myarray[3][1].
Does this make sense or am I over complicating things?
Your line myarray.push(nv[0],nv[1]); pushes two elements to the array myarray, not a single cell with two elements as you expect (ref: array.push). What you want is myarray.push( [nv[0],nv[1]] ) (note the brackets), or myarray.push(nv.slice(0, 2)) (ref: array.slice).
To simplify your code, may I suggest using Array.map:
var q = "foo=bar&baz=quux&lorem=ipsum";
// PS. If you're parsing from a-tag nodes, they have a property
// node.search which contains the query string, but note that
// it has a leading ? so you want node.search.substr(1)
var vars = q.split("&").map(function (kv) {
return kv.split("=", 2);
});
For searching, I would suggest using array.filter:
var srchkey = "foo";
var matches = vars.filter(function (v) { return v[0] === srchkey; });
NB. array.filter will always return an array. If you always want just a single value, you could use array.some or a bespoke searching algorithm.
for (var i = 0; i < fArray.length; i++) {
nv = fArray[i].split('=');
myarray.push([nv[0],nv[1]]);
}
nv.length = 0; is not required, since you're setting nv in each iteration of the for loop.
Also, use var i in the for-loop, otherwise, you're using / assigning a global variable i, that's asking for interference.

Update nested attributes in a JavaScript object

I want to change 'hello' to 'hey' programmatically, the solution should work with any number of nested elements (I just use 2 levels to keep it simple).
var data = {level1: {level2 : 'hello' }};
I have access to the 'data' variable, the path ('level1/level2') and the new value ('hey').
I tried to do:
var parents = 'level1/level2'.split('/');
var target = data;
for(var i=0; i<parents.length; i++){
target = data[parents[i]];
}
target = 'hey';
The idea was to travel to the root
target = data
then 1 level deep
target = data['level1']
...keep going
target = data['level1']['level2'] //data['level1'] === target
and modify the contents
target = 'hey'
But it looks like a lose the reference to the original object (data) when I do (target = target['level2']).
I guess I can build a string with the path and then evaluate it:
eval("data['level1']['level2']='hey');
Is there a better solution that dosen't involve eval()?
There are two issues. First is that you keep using data inside the loop, which means you're trying to access the top level keys instead of the inner keys. Change target = data[parents[i]]; to
target = target[parents[i]];
The second is that when you change the variable target, you're not changing the data variable but target instead. If you drop out of the loop one iteration earlier you can update the object which is stored as a reference:
for(var i=0; i<parents.length-1; i++){
target = target[parents[i]];
}
target[parents[i]] = 'hey';
Demo: http://jsfiddle.net/Lherp/
Try something like this:
var data = {level1: {level2 : 'hello' }};
var parents = 'level1/level2'.split('/');
var target = data;
for(var i=0; i < parents.length - 1; i++){
target = target[parents[i]];
}
target[parents[i]] = 'hey';
Or am I missing something?
edit: I was missing something (sorry, should have tested it first..)

iterating over JSON in javascript to put into select options

I keep having issues iterating over some JSON to put in select options
(btw, ignore the actual values for "label", those are garbage atm).
Here is an example that my php is passing into this:
[{"value":"1","label":"04-22-12"},{"value":"4","label":"04\/04\/12"}]
I am currently trying this loop:
*note, dateSelect is defined somewhere else
for (res in JSON.parse(request.responseText)) {
var date = document.createElement("option");
date.value = res.value;
date.text = res.label;
dateSelect.add(date, null);
}
However, it is adding "undefined" into my options...
How do I get it so each value and corresponding label is put in there correctly?
You have an Array, so don't for-in.
In your code, res is the property name (the index of the Array in this case) in the form of a string, so the properties you're looking for aren't going to be defined on the string.
Do it like this...
for (var i = 0, parsed = JSON.parse(request.responseText); i < parsed.length; i++) {
var res = parsed[i];
var date = document.createElement("option");
date.value = res.value;
date.text = res.label;
dateSelect.add(date, null);
}

Categories