Why can't a certain DOM element have CSS styles - javascript

While developing an Angular app I've come across the following issue: an SVG element didn't have styles from its class (even though it was defined in <styles> tag) and what's more peculiar (and the point of the question): Chrome and Firefox DevTools didn't allow adding element styles manually.
Note the missing element.styles block in the right pane.
However, if I edit container element HTML and just copy and paste back the markup - it suddenly appears and everything works as expected. So this must happen due to the way the element is added to DOM programmatically. And since this behaviour is identical in both Chrome and Firefox, it is most likely a feature, rather than a bug.
So how can this be achieved purposedly?
P.S. For those interested in Angular part, here is a GitHub issue that I reported for this case (also contains a reproduction repo): https://github.com/angular/material2/issues/15727

So I've found the answer myself. TL;DR: element was created with document.createElementNS with an unknown namespace (or empty), which made it an Element, rather than SVGElement. Styles are not defined for base Elements according to the specification.
So obviously the key was to check the type of the element - and both Chrome and Firefox have shown that that SVG is created as a base Element:
The next question was how did it happen. TL;DR for this section: due to a bug in Angular's new experimental compiler (probably) it was added with an invalid namespace and the rest is according to specification.
Search through Angular's source revealed the way it creates elements: createElementNS.
Quick experiment with that function has proven the initial guess that the problem was with the element's interface (Element), and it can be reproduced by passing an invalid namespace.
And DOM specification revealed exactly why does that happen:
createElementNS steps:
The internal createElementNS steps...:
...
4. Return the result of creating an element given document, localName, namespace, prefix, is, and with the synchronous custom elements flag set.
Creating an element is defined as follows:
To create an element, given a document, localName, namespace, and optional prefix, is, and synchronous custom elements flag, run these steps:
...
4. Let definition be the result of looking up a custom element definition given document, namespace, localName, and is.
Looked up definition turns out to be NULL in this case:
To look up a custom element definition, given a document, namespace, localName, and is, perform the following steps. They will return either a custom element definition or null:
1. If namespace is not the HTML namespace, return null.
Returning to createElementNS steps from before:
...
7. Otherwise: [definition is null]
Let interface be the element interface for localName and namespace.
Set result to a new element that implements interface, with no attributes, namespace set to namespace, namespace prefix set to prefix, local name set to localName, custom element state set to "uncustomized", custom element definition set to null, is value set to is, and node document set to document.
...
And finally, the element interface is defined as follows:
The element interface for any name and namespace is Element, unless stated otherwise.
As for inability to add custom styles, that is due to the missing style property on the base Element. That's probably due to the specification of the style attribute:
All HTML elements may have the style content attribute set. This is a style attribute as defined by the CSS Style Attributes specification.
HTML elements are:
To ease migration from HTML to XML, UAs conforming to this specification will place elements in HTML in the http://www.w3.org/1999/xhtml namespace, at least for the purposes of the DOM and CSS. The term "HTML elements" refers to any element in that namespace, even in XML documents.
However, style property exists in SVGElement, and probably some other base prototypes, so there must be some other specifications that allow that. But I thought that's out of scope of my original question.
And that concludes my research.

Related

Is there a practical use for the additional DOM subcategories in Devtools > Elements > Properties?

Like everyone else I use Chrome DevTools to inspect an HTML Element's properties. For example if I needed to know which properties were attached to a specific <DIV> I would do this.
Go to DevTools
Open the Elements panel
Click the HTML DIV element I wanted to inspect
In the right panel click the Properties tab
The top listing will be div.(classname)
Click that label and view the properties
Awesome! I can see all of the properties attached to that node and use them as needed.
But below that are 6 additional listings that I and possibly a lot of other people never use. They appear to represent part of the DOM hierarchy.
HTMLDivElement
HTMLElement
Element
Node
EventTarget
Object
Question: Do these also have a practical use when building a site? Am I missing out on something cool that I could use them for? Are they merely there for reference? Thanks for any input!
You have a list of object and interface types and contrary to your assertion that not a lot of people use them. They are extensively used (although you may not even realize it).
Object
In JavaScript, everything is an Object. Objects are endowed with basic properties and methods and literally every other object inherits these.
EventTarget
Is specific reference to the object that was the source for an event being triggered.
Node
In the W3C Document Object Model API (nice overview here), specific element types are not what is paramount. All elements and attributes (as well as other types of markup, such as DOCTYPE and comments) are generically referred to as "nodes" and every node has certain properties (i.e. nodeName, nodeType, nodeValue and methods). The DOM API favors treating all markup as fundamental nodes. This API is designed this way because it works with HTML but also XML.
Element
Is a generic term used to talk about any HTML element.
HTMLElement
Is an "interface" that describes the various ways you can programatically interact with any object that implements the interface (as all HTML elements do).
HTMLDivElement
Is a more specific interface that describes what an HTML DIV element should expose for programattic interaction.
Don't get confused between tag names, like <div> and DOM interfaces such as 'HTMLDivElement`. The first is part of creating a document structure and the second is an interface for the browser and scripting.
The HTMLDivElement interface provides special properties (beyond the
regular HTMLElement interface it also has available to it by
inheritance) for manipulating div elements.
Search through MDN or the spec itself and carefully read those descriptions of the others in your list.

Polymer create element dynamically

I need to create an custom-element dynamically.
I tried
var newElement= document.createElement('custom-element');
this is work. but My problem is when I want to add attribute to this element, to bind an array to this element.
I tried
newElement.setAttribute('data','{{data}}')
But it says that it expected to array and received '{{data}}'
How can I add this binding to dynamically elements?
I dont think this is possible right now, please see from kevinpschaaf:
https://github.com/Polymer/polymer/issues/1778
No, we don't currently support this, outside of dom-bind, which is the
only template implementation that late-binds instance children. You
can document.createElement('template', 'dom-bind'), then you can
dynamically append children with binding annotations to its content,
and the bindings will only be evaluated once the dom-bind is attached
to the document. See tests here that show this usage of it:
https://github.com/Polymer/polymer/blob/master/test/unit/dom-bind.html#L95
Note that dom-bind does not currently allow binding to outer scope, so
it has limited use in custom element templates (it's main use case is
for binding between elements in the main document), and that's not
likely to change short-term.
We are achieving a lot of performance optimization by baking the
binding connections into the prototype at registration time for an
element (rather than at instance time), and we haven't built up enough
of the machinery to easily allow runtime addition/removal of bindings.

D3.js - Is the "svg:" in "svg:rect" a must?

For example:
svg.append("svg:rect");
What if I drop "svg:"? It seems to work without "svg:". Any potential problems for dropping it?
Is "svg:" a d3 specific thing or it is generally required when svg
elements are used?
From the d3.js append() documentation
The name may be specified either as a constant string or as a function that returns the DOM element to append. When the name is specified as a string, it may have a namespace prefix of the form "namespace:tag". For example, "svg:text" will create a "text" element in the SVG namespace. By default, D3 supports svg, xhtml, xlink, xml and xmlns namespaces. Additional namespaces can be registered by adding to d3.ns.prefix. If no namespace is specified, then the namespace will be inherited from the enclosing element; or, if the name is one of the known prefixes, the corresponding namespace will be used (for example, "svg" implies "svg:svg").
So namespaces are (in most cases) optional since this commit.
The svg: part specifies the namespace to be used for the element. In particular, svg:rect means that the rect should be interpreted in the SVG namespace. This is relevant because not all types of elements exist in all namespaces (e.g. HTML does not have rects).
You do not normally need to specify this explicitly in recent versions of D3 as the namespace is derived from the context. In some cases, you do need to specify it to have the document interpreted properly. A notable example is when using foreignObject in SVG, which allows you to embed non-SVG content, see this example. Here it is necessary to specify xhtml as the namespace for the appended body element as the context is SVG and it would be interpreted incorrectly.

Interface of an element in JavaScript

In Chrome, when debugging in JavaScript, it is interesting to get the interface of an element.
Typing the variable name that holds the element in the console usually gives me the element tag. How can I get the interface matching the element. Sometimes Chrome outputs it, but sometimes gives the tag. I am unsure how Chrome returns the value.
Browsers try to be smart when displaying things via console.log to make the output more readable. If you want to consistently get a tree of properties that you can navigate through, you can use console.dir.
interface has no meaning in JS and a very specific meaning in other languages. You can potentially see the WebIDL interface of a DOM Element by viewing the prototype of an element using console.log(element.__proto__); but that is entirely browser dependent and non-standard.
If you want a standard way (i.e. not using __proto__):
console.log(el.constructor.name);
It looks like you can force one view or the other by specifying which you want:
console.dir(el)
gives you the "interface" for that el, while
console.dirxml(el)
prints the element as it would appear in the Elements panel.

Local id-s for HTML elements

I am developing a kind of HTML+JS control which can be embedded into various web pages. I know nothing about those pages (well, I could, but I don't want to). The control consists of one root element (e.g. DIV) which contains a subtree of child elements. In my script, I need to access the child elements. The question is: how can I mark those child elements to distinguish them?
The straightforward solution is using id-s. The problem here is that the id must be unique in the scope of the entire document, and I know nothing about the document my control will be embedded into. So I can't guarantee the uniqueness of my id-s. If the id-s are not unique, it will work (if used with care), but this does not conform with the standard, so I can meet problems with some new versions of the browsers, for example.
Another solution is to use the "name" attribute. It's not required to be unique -- that's good. But again, the standard allows the presence of "name" attribute only for a restricted set of element types. For example, the "name" attribute is invalid for DIV elements.
I could use, for example, the "class" attribute. It seems to be OK with the standards, but it's not OK with the meaning. "class" should be used for other purposes, and this may be confusing.
Can anybody suggest some other options to implement local id-s for HTLM elements?
You could use the HTML5 data-* attributes so you can give them a custom name with the right meaning:
https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
Do something like:
<div id="element-id" data-local-id="id-value">
...
</div>
and get the value in JavaScript with:
const el = document.getElementById('element-id');
const { localId } = el.dataset;
If you use a prefix to all of your ID's and or classes such as myWidgetName_98345699- the likelihood of collisions is highly improbable.
<div id="myWidgetName_98345699-container" class="myWidgetName_98345699-container">
jQuery does have selectors that will search for part of an ID, so using common names like container would be smart to stay away from as well. Using a longish alphanumeric mix for the specific part of the ID would be smart also
Typically, including hidden information within the web page required creative approaches. For example:
Storing information within HTML element attributes such as id, class, rel, and title, thus overriding the attributes original intent.
Using <span> or <div> blocks that contain the information, while making such blocks invisible to the user through styling (style="display: none;").
Adding JavaScript code to the web page to define data structures that map to HTML ID elements.
Adding your own attributes to existing HTML elements (breaking the HTML standard itself, and relying on the HTML browser to ignore any syntax errors).
The approaches above are not elegant and are not good coding practice, but the good news is that jQuery has a facility that simplifies associating data to DOM elements in a clean, cross-browser manner.
Use the custom data attributes:
Any attribute that starts with "data-" will be treated as a storage area for private data (private in the sense that the end user can't see it - it doesn't affect layout or presentation.
Defining custom data via html:
<div class="bar" id="baz" data-id="foo">
...
</div>
Associating data-id to specific DOM elements (jQuery):
$('#foo').data('id', 'baz');
Retrieving an element with specific data-id:
var $item = $('*[data-id="baz"]');

Categories

Resources