Retrieving map of attributes in DOM 4 - html

Historically in the XML DOM for any Element one can call element.getAttributes() which will return a NamedNodeMap. This map allows you to iterate the Node instances, which in this case are Attr instances describing each attribute.
But when reading MDN's description of the node type enumerated value for Node.ATTRIBUTE_NODE, they say:
An Attribute of an Element. Attributes no longer implement the Node interface as of DOM4.
Are they talking about Attr? But if Attr does not implement Node in DOM 4, what does element.getAttributes() return? A NamedNodeMap of nodes that are not instances of Attr?
Or does Element not have a element.getAttributes() method at all in DOM 4? If not, how does DOM 4 let me iterate through all the attributes? And is the code I'm writing in Java 11 using element.getAttributes() therefore eventually going to be out of date? And the same thing for JavaScript code I write in the browser?

There's various points here.
DOM4 does indeed not contain a getAttributes() method on Element. It does, however, have a property called attributes which on accessing returns a NamedNodeMap.
"NamedNodeMap" seems a slightly misleading name since its items are Attr objects.
MDN is out of date. Earlier versions of the Attr interface in the DOM4 standard did not extend the Node interface, but it was found not to be web-compatible, and so now the current definition of Attr does, just as DOM3 did.

Related

Use of hyphenated property keys for element.style

I am familiar with the use of setting inline styles on elements using the elements style property. For style properties which are hyphenated in a style sheet, the most common example is to use the dot-property, using a camel cased version of the property, eg,
element.style.fontSize = "14pt";
And like any other object, the same can be accomplished using the bracketed key form, eg:
element.style["fontSize"] = "14pt";
However, I discovered by accident, using the hyphenated property as a key also works in Firefox:
element.style["font-size"] = "14pt";
I am wondering if this form is reliable in all browsers. Ie, for element.style, will the hyphenated version alongside of the camel-cased version of the property always be present, and point to the same value? Is it part of the spec, or is this just an implementation detail for Firefox? My research of the use of inline styles in w3.org and MDN have not made this detail clear to me, as all examples I have seen only show the dot-property using the camel-cased version.
The bracketed key form is useful in some cases such as iterating over various properties, and to not have to deal with both the hyphenated and camel cased versions of the keys would be especially helpful, however I want to be sure this is reliable.
Edit: I'm hoping to find something definitive, like something in the spec which directly addresses this, or something in the definition of the element.style property which would make it absolutely clear.
Edit: It was suggested that my answer could be found in How do I reference a JavaScript object property with a hyphen in it? , however this does not actually answer my question. This (like the title states) asks a general js question as to how to deal with js properties with hyphens (even though the op happened to use element.style as an example.) What I am asking is specific to element.style, and whether in all browsers, will the hyphenated version of the property always exist alongside of the camel-cased version, and point to the same value?
The documentation links to the relevant specification.
An HTMLElement.prototype.style getter results in a CSS2Properties object which inherits from CSSStyleDeclaration, the specification of which has a few relevant paragraphs about camel-cased properties:
The camel-cased attribute attribute, on getting, must return the result of invoking getPropertyValue() with the argument being the result of running the IDL attribute to CSS property algorithm for camel-cased attribute.
Setting the camel-cased attribute attribute must invoke setProperty() with the first argument being the result of running the IDL attribute to CSS property algorithm for camel-cased attribute, as second argument the given value, and no third argument. Any exceptions thrown must be re-thrown.
… and about dashed properties:
The dashed attribute attribute, on getting, must return the result of invoking getPropertyValue() with the argument being dashed attribute.
Setting the dashed attribute attribute must invoke setProperty() with the first argument being dashed attribute, as second argument the given value, and no third argument. Any exceptions thrown must be re-thrown.
The mentioned IDL attribute to CSS property algorithm is a way of normalizing a property key before getting or setting the actual key and value. Therefore, the equivalence of the camel-cased and hyphenated version of properties is specified.
But there’s a single exception: float. Because ECMAScript editions 1, 2, and 3 disallowed reserved words as property names, using element.style.float was impossible; since the 5th edition it’s possible. The usable property is called cssFloat and the spec has this to say about cssFloat:
The cssFloat attribute, on getting, must return the result of invoking getPropertyValue() with float as argument. On setting, the attribute must invoke setProperty() with float as first argument, as second argument the given value, and no third argument. Any exceptions thrown must be re-thrown.
However, older browsers don’t follow this specification (or convention before the spec existed) very well. See Where do the JavaScript style property names come from?: old Internet Explorer versions use styleFloat instead of cssFloat.
CSS didn’t always have a specification; many parts were underspecified or simply unspecified and relied on conventions among browser vendors. It’s not easy to accurately find out the implementation reality of this feature, but after some “Internet history” digging, I found:
According to the compatibility table for CSSStyleDeclaration, Internet Explorer ≤8 didn’t support it (properly), but all other browsers did since the beginning.
The current CSSOM specification remained unchanged in this part since the beginning (September 2015).
A previous version of the specification had a complete table to translate properties from one form to the other, but the property normalization remained the same (July 2011).
A specification which goes back to August 2001 has one relevant passage about getting and setting properties and a table of all camel-cased CSS properties implemented on the same interface. The passage is about CSS2Properties and says:
The CSS2Properties interface represents a convenience mechanism for retrieving and setting properties within a CSSStyleDeclaration. The attributes of this interface correspond to all the properties specified in CSS2. Getting an attribute of this interface is equivalent to calling the getPropertyValue method of the CSSStyleDeclaration interface. Setting an attribute of this interface is equivalent to calling the setProperty method of the CSSStyleDeclaration interface.
getPropertyValue and setProperty do accept the hyphenated CSS properties in this version, and, as the quote says, “the attributes of this interface correspond to all the properties specified in CSS2”. However, it seems that the exact correspondence is implied; i.e. the fact that fontSize maps to font-size, or that they’re supposed to yield the same result, may be underspecified. It also seems to imply that "fontSize" is an acceptable argument to getPropertyValue, which it isn’t.
Furthermore, the specification only was a proposed recommendation shortly before that (i.e. before 13 November, 2000), and in terms of conformance to the DOM Level 2 CSS spec, this CSS2Properties interface was optional:
The interface found within this section are [sic!] not mandatory.
It doesn’t sound as if it’s very likely that browsers from the early 2000s support this equivalence between hyphenated and camel-cased CSS properties.
Works in
Chrome 92 Mac
Safari 14.1.2 Mac
Firefox 89 and 91 Mac
const element1 = document.getElementById("e1");
const element2 = document.getElementById("e2");
const element3 = document.getElementById("e3");
element1.style.fontSize = "14pt";
element2.style["fontSize"] = "14pt";
element3.style["font-size"] = "14pt";
p { font-size: 9pt;}
<p>Text</p>
<p id="e1">Text</p>
<p id="e2">Text</p>
<p id="e3">Text</p>

Pass element refrence to self from DOM back to view model

Known feature:
<div #element (click)="myMethod(element)"></div>
this passes the div back to the VM for manipulation, etc.
What I'm looking for is short hand to pass an element to a method without declaring a template variable. It could look like this:
<div (click)="myMethod($self)"></div>
This would be helpful in cases where creating elements in an ngFor stops you from giving every element a variable name or in cases where using a third party library that sends its own $event and the element ref is missing. Does anyone know of any way to do this?

Is there a way to have two custom elements sharing a DOM element, e.g. an <iron-ajax>

I have two custom elements having similar functionality and both having an <iron-ajax> element(in local DOM template) to make service calls, I was trying to extract these common part into a behavior. But I also realize that polymer behavior does not carry local DOM template. Is there a way to let the behavior have the <iron-ajax>? dynamically create it using Document.create?
As "behavior" is the Polymer way of doing code sharing, can a DOM element be shared across elements?
You should create an other element for your API calls which have inside the <iron-ajax> and have your api call methods, then you need just change the iron-ajax element in your elements and use your <api-element> for this.
Below are two methods which i can think of
Create iron-ajax element at root level of your application and then refer to that element from each of your element using querySelector or getElementById on document
In your behavior create iron-ajax element from javascript with createElement function of javascript.
In both the cases you can add event listener on response and error.

Polymer 1.0: Does <iron-meta> support binding to dynamic variables?

I can get my <iron-meta> instance to work properly when using a static value. But when I bind the value to a dynamic variable (using {{}}) it <iron-meta> no longer behaves as expected.
Does <iron-meta> support binding its value to dynamic variables?
<iron-meta id="meta" key="info" value="foo/bar"></iron-meta> // works
<iron-meta id="meta" key="info" value="{{str}}"></iron-meta> // fails
Previous work
This question is a refinement of this question in order to clarify that the ONLY thing causing the problem is the change from a static string value to a dynamic string value binding. I was getting a lot of other suggesting that had nothing to do with the change from static to dynamic so I thought it might be best to rewrite the question to clarify that. But the entire code context is contained in the links there if that would help.
Alternative solutions
There has been some recent chatter about using <iron-localstorage>. Perhaps that is the best way to go for dynamic binding essentially creating global variables?
Yes, <iron-meta> does support binding to variables, but perhaps not in the way you think.
Example: http://plnkr.co/edit/QdNepDrg9b3eCTWF6oRO?p=preview
I looked through your code here, here, and here but I'm not entirely clear what your expectations are. Hopefully my attached repro might shed some light. I see you have declaratively bound <iron-meta id="meta" key="route" xvalue="foo-bar" value="{{route}}"></iron-meta> which is fine - when route changes, iron-meta's key="route" will update accordingly.
However, be aware that in Polymer 1.0, <iron-meta> is in essence a one-way bind from parent to child in the sense that you set a meta key value dynamically by binding to a property; but to get that value, you'll have to get it imperatively via iron-meta's byKey() method.
<iron-meta> is just a simple monostate pattern implementation without an in-built path notification mechanism. What this means is value changes do not propagate upwards. Therefore, doing something like
<!-- this does not work like the way you think -->
<iron-meta id="meta" key="foo" value="{{bar}}">
in order to get the value of foo, or listen to changes to foo, does not work. This behaves more like a setter, where you set the value of foo based on your data-bound property bar.
From what I gather, it seems that you're trying to implement some sort of global variable functionality. A monostate implementation used to work in Polymer 0.5, but not in 1.0. Unfortunately, until Google endorses a "best-practice" pattern for this, suggestions till-date seems a bit speculative to me. You might find this (Polymer 1.0 Global Variables) helpful.
I have had success using <iron-signals> to communicate global information. I know there is a warning in the <iron-signals> documentation that discourages its use for related elements, but when broadcasting a shared resource it seems just the thing. For example:
// source element
var db = SomeDB.init();
this.fire('iron-signal', { name: 'database', data: db });
<-- sink element -->
<iron-signals on-iron-signal-database="dbChange"></iron-signals>
class SinkElement {
dbChange(e, detail) {
this.db = detail;
this.db.getSomeData();
}
}

How to handle testing an HTML control that lacks sufficiently unique attributes?

I'm using Coded UI Test to test a web application.
I have a class Locator that I use to stash the specifics needed for CUIT to find a control. To operate on a control, a page object specifies the locator, not the control, and lower-level functions find the control and perform the operation.
Right now, my class has:
Locator name.
One or more attrName/attrValue pairs that can locate the HTML element for the control.
An operator (Contains or EqualTo) that specifies the matching needed.
The problem: Some of the controls I need to operate on don't have enough unique attributes to allow them to be found. (Yes, I know the developers should improve their HTML, but I don't control that.) I have been using a locator to find a nearby element, then "walking" in the DOM to get to the element I want. I hate having this DOM-walking code in my page object, even factored into a function.
A possible solution: I'm considering enhancing class Locator so that it can have either the attrName/attrValue pairs or a reference to a function that has the DOM-walking code. One advantage of this is that the page objects would always use a locator object. Another is that when the HTML is improved, the locator could change from DOM-walking code to attrName/attrValue pairs, which would be transparent to the page object.
Still, I think this may be over-complicated.
Is there a better solution to this problem?
Not sure specifically how your locator works, but could you find the closest parent to that object, let's say an HTML Div with an id of "parent", and then count the tag instances underneath? For example:
HtmlDiv id="parent">
HtmlHyperlink>text1</
HtmlHyperlink>text2</
Would require the following code:
public HtmlHyperlink text2Link
{
get
{
HtmlDiv parentDiv = new HtmlDiv(browser);
parentDiv.SearchProperties["id"] = "parent";
HtmlHyperlink target = new HtmlHyperlink(parentDiv);
target.SearchProperties["TagInstance"] = "2";
}
}
This would find the 2nd hyperlink under the parent object. (Tag instances are not zero based).
Then, you'd just interact with your object as needed:
Mouse.Click(text2Link);
for example.