TableRow cells Collection set attribute - html

I'm looping through a table in the form of table.rows.length and inside it rows.cells.length and when a certain cell meets a certain criteria then I would like to set an attribute to that html of that cell.
I know you can change the innerHTMl like
var x = document.getElementById("myTable").rows[0].cells;
x[0].innerHTML = "NEW CONTENT";
so I thought the attr would be like
x[0].attr = ('name', 'value');
But no such luck.
could someone please point me to the right direction?
If there are any resources you can recommend that give a full list of all the options you can add to a cell this way that would be great!

Since you're operating with HTML nodes directly - you're dealing with HTMLElement objects that are, in their turn, inherited from generic Element.
As you can see from documentation - you can reach attributes through Element.attributes map, each of them are Attr object with name and value properties.
So correct way will be to use:
x[0].setAttribute('name', 'value');

Working with jQuery and es6 syntax you could use the map function:
var xtr = $('#mytable tr');
xtr.map(itr => {
xtd = $(itr).children('td');
xtd.map(itd => {
$(itd).attr('key', 'value');
});
});
If thats confusing you can work with the for loop the old way:
var xtr = $('#mytable tr');
for(i in xtr){
xtd = $(xtr[i]).children('td');
for(j in xtd){
$(xtd[j]).attr('key', 'value');
}
}
Point being when using jQuery what you do is:
$('element').attr('key', 'value');

Related

Check <head> contains specific <link> without JQuery

Is it possible to check that the <head> contains a specific <link> tag?
For example I would add a link to the head:
let link = document.createElement('link');
link.href = 'https://my.path.css';
link.type = 'text/css';
link.rel = 'stylesheet';
document.head.append(link);
Is it then possible to check that it is present within the head?
I know that I can get an HTMLCollection with
const headChildren = document.getElementsByTagName('head')[0].children;
But I can't seem to navigate this to check...
The reason you can't "navigate" headChildren is because it's an 'iterable' but not officially an array.
Use Array.from to convert it then you can treat it like a normal array.
let actualArray = Array.from(headChildren)
You can also iterate over 'iterables' with a for of loop:
for (let child of headChildren) {
console.log(child)
}
I would use querySelectorAll and not children. I would convert the HTML collection to an array and use some to loop over the collection to find a match with the href.
const exist = Array.from(
document.querySelectorAll('link[rel="stylesheet"]')
).some(link => link.href === 'https://my.path.css')
The suggestions for the loop were only half of the solution I required.
I wanted a simple way to check that the exact tag that I had created and pushed into the was present.
I attempted all the different types of equality to no avail. '==' and '===' wouldn't work I'm assuming because of referring to object identities etc like Strings suffer from.
I found the solution with `.isEqualNode' which equates two nodes.
Therefore my solution in the end was:
const headChildren = document.getElementsByTagName('head')[0].children;
for (let child or headChildren) {
if (child.isEqualNode(myLinkNode) {
// Do something
}
}

Error with custom Search and Replace function for Google Sites

I'm trying to use a script to replace a particular string with a different string. I think the code is right, but I keep getting the error "Object does not allow properties to be added or changed."
Does anyone know what could be going wrong?
function searchAndReplace() {
var teams = SitesApp.getPageByUrl("https://sites.google.com/a/directory/teams");
var list = teams.getChildren();
list.forEach(function(element){
page = element.getChildren();
});
page.forEach(function(element) {
var html = element.getHtmlContent();
html.replace(/foo/, 'bar');
element.setHtmlContent = html;
});
};
Try This:
Javascript reference:
The replace() method returns a new string with some or all matches of a pattern replaced by a replacement.
I think the issue here is that forEach cannot change the array that it is called upon. From developer.mozilla.org "forEach() does not mutate the array on which it is called (although callback, if invoked, may do so)."
Try doing it with a regular loop.

Cesium: Theming the InfoBox

I have seen a few examples on Google Groups which demonstrate how to modify the css of the infobox. In this particular example, javascript is used to append a css link to the head of the document:
https://groups.google.com/forum/#!topic/cesium-dev/f0iODd42PeI
var cssLink = frameDocument.createElement("link");
cssLink.href = buildModuleUrl('Path/To/Your/CSS/File.css');
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
viewer.infoBox.frame.contentDocument.head.appendChild(cssLink);
This, however, has not resulted in any changes to the style of my markup.
At best, I have been able to wrap the contents of the infobox by iterating through the entities in the .then function call subsequent to loading a geoJson dataset. When wrapping the contents, I can set style values which are readily apparent in the resulting markup.
var dataSource = Cesium.GeoJsonDataSource.load('../data/mGeoJson.json').then(function(data) {
viewer.dataSources.add(data);
var entities = data.entities.values;
for (var i = 0; i < entities.length; i++)
var entity = entities[i];
if (entity.properties.hasOwnProperty("description")) {
entity.description = '<div style="height: 360px;">' + entity.properties.description
+ '</div>';
}
}
}
This is useful, but does not completely satisfy the requirements of my app.
Could someone provide additional insight into overriding the theme of the infobox, without having to iterate over entities to modify the value of their description properties?
The original solution here wasn't working, because the infoBox is an iframe that has not yet asynchronously loaded when you were trying to modify it.
Instead, you can add an load listener to the iframe, like this:
var viewer = new Cesium.Viewer('cesiumContainer');
var frame = viewer.infoBox.frame;
frame.addEventListener('load', function () {
var cssLink = frame.contentDocument.createElement('link');
cssLink.href = Cesium.buildModuleUrl('Path/To/Your/CSS/File.css');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
frame.contentDocument.head.appendChild(cssLink);
}, false);
This waits for the iframe to become ready to receive the modification, and then applies it.
For what it's worth, I've found success in modifying the theme of the infobox by simply importing my css files in the head of the document. I'm not sure why I wasn't able to modify it directly with stylesheets, as it wasn't previously affecting the infobox's appearance, and this issue was mirrored in the posts that I found in the cesium-dev Google Group. Regardless, it seems to be working just fine now.

How do I format my AngularJS data model?

Hi I am just beginning with angular and I am struggling to find the answer to what I'm sure is quite a simple thing to do.
I am currently getting the values of some input boxes and pushing them into my scope. This is creating one long 'array' eg:
['data-1','data-2','data-3']
I would like to format my data in the following way instead
$scope.data = [
{
'header1': 'data1-1',
'header1': 'data1-2',
'header1': 'data1-3'
},
{
'header1': 'data2-1',
'header1': 'data2-2',
'header1': 'data2-3'
}
]
This is my function as it currently is.
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
$scope.td.push($(value).val());
});
}
Any help or pointers would be greatly appreciated as I am just getting my head round the angular way
Doing this isn't hard... but before I give you a gun to shoot yourself in the foot, just to say that I think it would be beneficial to explain WHY you want structure in that other format you are mentioning. You seem to have lots of data repetition and that's always a red flag.
Now for the code, you just need to create object before pushing it to the array like:
$scope.createRow = function(){
angular.forEach(angular.element("input"), function(value, key){
var obj = {
"header1": val + "-1",
"header2": val + "-2"
};
$scope.td.push(obj);
});
}
EDIT:
OK, so you are trying to add new row to the table. First of all, you shouldn't be doing angular.forEach, but rather those input elements in HTML should bind to existing scope object, like:
// obviously use better names than Input1Value
// I am here just giving you example
$scope.bindData = {
Input1Value: null,
Input2Value: null
};
// in HTML you will do
// <input ng-model="bindData.Input1Value" />
// <input ng-model="bindData.Input2Value" />
Now that you've eliminated that nasty angular.forEach you need to have some kind of event handler, for example when user clicks the button you want to add this object to the array to which table is data bound. Just be sure to clone the $scope.bindData object when you add it to array.
$scope.createRow = function(){
var newRowData = $scope.cloneObject($scope.bindData);
$scope.td.push(newRowData);
}
// http://heyjavascript.com/4-creative-ways-to-clone-objects/
// https://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
$scope.cloneObject = function(objToClone) {
var newObj = (JSON.parse(JSON.stringify(objToClone)));
}
To close this answer off - keep in mind, if you ever find yourself directly referencing HTML DOM elements in Javascript with AngularJS - you are doing something wrong. It's a nasty habit to eliminate, especially if you are coming from jQuery background (and how doesn't?), where everything is $("#OhHiThere_ElementWithThisId).
Obviously the main thread on this topic on StackOverflow is this one:
“Thinking in AngularJS” if I have a jQuery background?
However I find that it's too theoretical, so Google around and you may find better overviews like:
jQuery vs. AngularJS: A Comparison and Migration Walkthrough

Using controller-scoped data in a directive's jqlite-generated html

This question is similiar to them one asked in Mike's post Using ng-model within a directive.
I am writing a page which is small spreadsheet that displays calculated output based on user input fields. Using a directive, I'm making custom tags like this:
<wbcalc item="var1" title="Variable 1" type="input"></wbcalc>
<wbcalc item="var2" title="Variable 2" type="input"></wbcalc>
<wbcalc item="calc" title="Calculation" type="calc"></wbcalc>
The 'item' field references scoped data in my controller:
$scope.var1 = '5'; // pre-entered input
$scope.var2 = '10'; // pre-entered input
$scope.calc = function() {
return parseInt($scope.var1) + parseInt($scope.var2);
};
And the 'type' field is used in the directive's logic to know whether to treat the item as a string or a function.
Here's a fiddle for this: http://jsfiddle.net/gregsandell/PTkms/3/ I can get the output elements to work with the astonishing line of code:
html.append(angular.element("<span>")
.html(scope.$eval(attrs.item + "()"))
);
...and I'm using this to get my inputs connected to my scoped controller data (I got this from Mike's post:
var input = angular.element("<input>").attr("ng-model", attrs.item);
$compile(input)(scope);
html.append(input);
...while it does put the values in the fields, they aren't bound to the calculation, as you can see by changing inputs in my fiddle.
Is there a better and/or more intuitive way to link my controller-scoped data to the jqlite-generated html in my directive?
Take a look at this, I think you can simplify the process a fair bit.
http://jsfiddle.net/PTkms/4/
angular.module('calculator', []).directive('wbcalc', function($compile) {
return {
restrict: 'E',
template: '<div><div class="span2">{{title}}</div><input ng-model="item"></div>',
scope: {
title: '#',
item: '='
},
link: function(scope, element, attrs) {
// Don't need to do this.
}
}
});
function calcCtrl($scope) {
$scope.var1 = '5';
$scope.var2 = '10';
$scope.calc = function() {
// Yes, this is a very simple calculation which could
// have been handled in the html with {{0 + var1 + var2}}.
// But in the real app the calculations will be more
// complicated formulae that don't belong in the html.
return parseInt($scope.var1) + parseInt($scope.var2);
};
}
I know you said you like jQuery - but to make best use of Angular you need to think in an Angular way - use bindings, don't manipulate the DOM directly etc.
For this example, it would be helpful to read up on the isolated scope bindings used - '#' and '=', see:
http://docs.angularjs.org/guide/directive