In a browser that is displaying an XHTML document with namespaced elements, how can I use JavaScript to access the .style properties of an element outside the HTML namespace?
document.getElementsByTagNameNS(namespace, tagName) returns a collection of objects but the objects don’t have a CSSStyleDeclaration style property (at least not in Chrome or Firefox).
You could say that is by design, that CSS is specific to HTML. But namespaced CSS styles the content just fine. So the style information is in there somewhere. How do I read and write it?
I want to know, for example, which elements are being rendered as blocks and which inline.
(Edit to add example:)
To see this, go to www.johnmccaskey.com/style.xhtml. The blue part is in HTML namespace, the red part in http://www.tei-c.org/ns/1.0 namespace. CSS styled them both just fine. In, say, Chrome’s console, enter document.getElementsByTagName("box"). You’ll see the two <box> objects. The HTML one has a .style property, the TEI one does not.
The .style property is a reflection of the content attribute on the HTML element, e.g. <div style="color:green">, not a reflection of its computed styles obtained from the cascade.
To get the computed values, use window.getComputedStyle().
To see it in action, add this script to your XHTML, just before the </body> tag
<script>
var boxes = document.body.children;
console.log(window.getComputedStyle(boxes[0], null).getPropertyValue("color"));
console.log(window.getComputedStyle(boxes[1], null).getPropertyValue("color"));
</script>
And inspect the console output.
To tell whether an element is inline or block or some other display value, use window.getComputedStyle(element, null).getPropertyValue("display")
Related
I'm modifying SVG elements with JavaScript for a program that runs in the Browser. For some element attributes like the href of an image element one has to specify the namespace and therefore use the setAttributeNS method.
Similar to setAttribute and setAttributeNS there are methods removeAttribute and removeAttributeNS. However my testing in Chrome and Firefox shows me that leaving out the namespace and just using removeAttribute is fine.
The question is whether this behaviour is by specification or something that Chrome and Firefox added for convenience. And whether I could run into problems in other Browsers or future versions that might drop this behaviour.
Example
This can be tested in the Browser console:
img = document.createElementNS( 'http://www.w3.org/2000/svg', 'image')
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/SVG_logo.svg/200px-SVG_logo.svg.png')
Outputting the content of variable img to the console will result in:
img
> <image href="https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/SVG_logo.svg/200px-SVG_logo.svg.png"></image>
If you want to check whether the namespace has been correctly set you can use:
console.dir(img)
Or use img.outerHTML which will show the namespace prefix:
img.outerHTML
> '<image xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/SVG_logo.svg/200px-SVG_logo.svg.png"></image>'
Now to the crucial part of removing the attribute:
img.removeAttribute('href')
Afterwards the output shows that the attribute was removed fine, despite not having used removeAttributeNS.
img
> <image></image>
Background
Of course I could just use removeAttributeNS if I want to be on the safe side. The reason I'm asking is that I've written something like a wrapper class for the DOM nodes. The wrapper registers all modifications of attributes and can remove them later. If I don't have to use removeAttributeNS, the class will be much more lightweight, because then it does not have to save the namespace of each attribute set with setAttributeNS.
I am just beginning to learn client-side JavaScript and using an online tutorial, so please bear with me.
This question is based on my understanding of the following:
To access the properties of the document's body, the syntax is "document.body", which returns all the elements in the body.
Similarly when you access the head, you use "document.head". Makes sense and most importantly, it works.
However, when I attempt to access elements WITHIN the body or head following the same logic, I get a return value of "undefined". For example, document.body.h1, returns "undefined", in spite of there being an h1 element inside the body element.
Further, when I enter document.head.title -- "undefined".
Strangely, however, when I enter "document.title", it returns the string value associated with the title tag.
I thought in order to access the title, you would have to access it through the head, since it is an element nested inside the head. But ok, that's fine. Using the same logic, I should then be able to enter document.h1 and get its value. Nope, instead, I get undefined.
Would someone be kind enough to explain to me why this behavior is so inconsistent. Thanks in advance.
You've really asked two questions:
Why document.title rather than document.head.title?
and
Why doesn't document.body.h1 return an element if there's an h1 in the body?
document.title
document.title is historical. Various parts of the browser environment were developed somewhat ad hoc by multiple different people/organizations in the 1990s. :-) That said, it's the title of the document, so this isn't an unreasonable place to put it, even if you use the title tag in head.
document.body.h1
One answer is: Because no one decided to design it that way. There were some early things like document.all (a list of all elements in the document) and even tag-specific ones (I forget exactly what they were, but they weren't a million miles off your document.body.h1 — I think document.tags.h1 or something, where again it was a list.)
But another answer is: Because the DOM is a tree. body can have multiple h1 elements, both as direct children and as children of children (or deeper); collectively, descendants. Creating automatic lists with all of these proved not to be scalable to large documents.
Instead, you can query the DOM (either the entire document, or just the contents of a specific element) via a variety of methods:
getElementById - (Just on document) Get an element using its id attribute value.
querySelector - Find the first element matching a CSS selector (can use it on document or on an element). Returns null if there were no matches.
querySelectorAll - Get a list of all elements matching a CSS selector (can use it on document or on an element). You can rely on getting back a list; its length may be 0, of course.
getElementsByTagName - Get a list of all elements with a given tag name (such as "h1").
getElementsByClassName - (No support in IE8 and earlier) Get a list of all elements with a given class.
There are many more. See MDN's web documentation and/or the WHAT-WG DOM Standard for more.
Some of the automatic lists persist (they got so much use that they had to be maintained/kept), such as document.forms, document.links, the rows property on HTMLTableElement and HTMLTableSectionElement instances, the cells property on HTMLTableRowElement instances, and various others.
document.head.title is a thing... but not what you might think.
title is an attribute that is applicable to all html elements; that is, it is a global attribute. It's meaning is 'advisory information'; one use is to display a tooltip:
<span title="hover over me and you'll see this">information</span>
So, all elements have a title attribute - including head. The title element - which is completely different - should be a child of the head though. So you might be tempted to set its value via document.head.title = "my title" , but document.head.title is not the head's title element, it's a property of the head element.
What you're actually doing is setting the title property on the head element:
<head title="my title">.... </head>
... which isn't what you want at all.
The correct way to set the title is document.title, which is a shortcut way of doing
document.querySelector("title").innerText = "my title"
It's said by this article that one of the important reasons for HTML properties to be reflected back to the DOM is because CSS selectors rely on attributes, but why's that? This could be done without the reflection based on the spec.
For people who don't know what I'm talking about, read below:
In browsers, CSS selectors rely on attributes to work.
#myButton[someAttribute] {
opacity: 0.5;
font-weight: bold
}
So in our JavaScript if we change the property of an element, eventually we have to reflect it to the HTML DOM as well like this:
// we have changed some property
myButton.someAttribute= true;
// but this is not adequate, we need to reflect as well
myButton.setAttribute('someAttribute', '');
so we get this:
<button id="myButton" someAttribute></button>
not this non-reflected button:
<button id="myButton"></button>
Not all DOM properties map to attributes. The ones that do reflect to and from attributes, do so to maintain parity with the document language — in this case, HTML, which only has a concept of attributes, which as you've correctly pointed out is relied on by Selectors.
If attribute selectors mapped directly to DOM properties without the DOM discriminating between attribute properties and other kinds of properties, then attribute selectors such as the following would match, even though none of these exist as attributes in HTML:
[classList]
[className]
[dataset]
[offsetLeft]
[offsetTop]
[offsetWidth]
[offsetHeight]
... as well as [someAttribute] matching elements with your non-existent someAttribute as a property even when you don't reflect it with setAttribute().
In fact, this is exactly why label[htmlFor] incorrectly matches label[for] elements in Internet Explorer 7, even though the for attribute in HTML is simply called for, not htmlFor — the DOM uses htmlFor to make up for the fact that for is a reserved word in many languages including JavaScript, the main DOM scripting language, preventing it from being used as a property ident.
DOM attributes and properties are not equivalent, but they're related.
Attributes are intended to be used to initialize DOM properties. When the HTML is parsed, all the attributes are used to initialize the corresponding DOM properties. If you later modify an attribute with setAttribute or removeAttribute, the corresponding property is also updated (similar to reloading the HTML with the new attribute).
But it doesn't go the other way. Updating a property doesn't change the corresponding attribute. This is why you can assign to the .value of an input, and see this reflected in the browser display, but when you look at the element in Developer Tools you still see the original value="whatever" attribute. In some cases this has special benefits -- when you click on the Reset button of a form, it resets all the value properties from their value attributes.
IMHO; Some attributes have a 1:1 mapping with their respective properties in the DOM. The reflection is automatically made for common attributes like id. You can also define your own attributes (your HTML will be considered invalid, but you can use the doctype to validate them). My guess is that due to this uncertainty created by rogue attributes. They preferred to map only the attribute:property which has predictable behaviour and usage. You can still use your custom attributes in your css but you're in manual mode. You got to keep your s**t together and reflect them yourself. This far west(freedom) mentality is one the things that made web tech so popular and easy to use. You can do things as you see fit. I do not recommend it for maintainability reasons but you could.
Your example uses a button, but the article is using the disabled property but with something other than a button. On a button, the browser will automatically reflect changes to the disabled property onto the attribute, and vice versa, but this doesn't happen with all elements. Change your example to use a div and you'll see that you'd need to manually coordinate the two if desired.
Or for custom attributes, use data- attributes instead. If you delete the property from my_element.dataset, I'm pretty sure the attribute will be deleted too.
This is to keep the HTML and DOM synchronized, because at some point CSS selectors will be checking the DOM element and relying on the attributes to be accurate.
If the DOM isn't accurate, then the CSS won't be accurate either. What if HTML didn't bother to reflect attributes back to the DOM?
Let's say the text of an input field is initially black, and you want the text to be red when it is disabled. Now let's say the user did something and a function you wrote disabled the input field through javascript.
If HTML didn't reflect that 'disabled' attribute back to the DOM, CSS would NEVER KNOW that the element was disabled.
So the text color would never be changed to red. Remember, CSS checks and relies on DOM attributes. If HTML doesn't change the DOM attributes, for all CSS cares about, nothing has changed so everything will remain the same.
For a less technical analogy, let's say CSS is Batman, HTML is Gotham Police Department, an Attribute is the bat-signal, and the DOM is the sky.
Batman(css) constantly checks the sky(dom) to see if his bat-signal light(attribute) is being shown by the Gotham Police Department(html). If there was some event(an attribute changed) which happened in Gotham where the Gotham Police Department(html) needed Batman(css) to help, but they just didn't bother to send him an update through the sky(dom) with the bat-signal(attribute update), Batman would never know there was a job that needs to be done.
I mean he's an awesome super hero so he would eventually find out but sadly, CSS is no Batman.
The article speaks about custom elements, and takes the example of a <div> element with it's natural behaviour for some properties like hidden or disabled.
So, first of all, don't take the sentence you mention as a directive from your god, because it's not.
Simply, if you have an application with some css using the disasbled property for specific styling, be aware that, if you want to :
create some custom elements
manipulate their attributes through Javascript, including disasbled
see the css applied for disasbled property of custom elements you are manipulating
Then, yes, you'll need to reflect back to DOM
Well, this is the first question I'm answering but I'll try either way.
To be honest, it's kinda hard to tell what you're asking but if you're looking to reflect HTMLElement property changes back on the DOM (via attributes). Then here's the code (using HTMLElement's):
// Defines a new property on an Object.
Object.defineProperty(HTMLElement.prototype, "someAttribute", {
// Configurable
configurable: true,
// Enumerable
enumerable: true,
/* Getter
(Allows you get the value like this =>
element.someAttribute // returns the value of "someAttribute"
)
*/
get: function() {
return this.getAttribute("someAttribute")
},
/* Setter
(Allows you to modify/ update the value like this =>
element.someAttribute = "lorem ipsum"
)
*/
set: function(data) {
this.setAttribute("someAttribute", data)
}
})
Hope this answered your question.
I would like to know the style property of the focused element. my code doesn't work:
alert(document.activeElement.id.style.border);
but it shows the id using this code:
alert(document.activeElement.id);
Any help?
I prefer not to use jquery . I'm doing a project on IE 7 . and I know a lot of you thinks IE 7 is not a browser .
The id is a property of the relevant node, the style is also a property of the node, so replace id with style.border (the id has no properties of its own, except those properties inherent to strings, as it is, merely, a string) to give:
document.activeElement.style.border;
As it was written, you were trying to access the style property of the string, which is non-existent and therefore undefined.
To access the individual properties of the border:
document.activeElement.style.borderStyle;
document.activeElement.style.borderWidth;
And so on, to access the individual properties of individual borders (border-left, border-right etc):
document.activeElement.style.borderLeftWidth;
document.activeElement.style.borderLeftStyle;
And, again, so on...
To respond to the comment left by the OP (in another answer):
but why this code: alert(document.activeElement.style.borderColor); shows a blank alert?
The problem may be that the styles are defined in a stylesheet, whereas the style property only accesses those styles in the style attribute of an element. In contemporary browsers, you need to look at the window.getComputedStyle() to see the computed rendered output of the styling, for example:
window.getComputedStyle(document.activeElement, null).border;
Internet Explorer has the alternative (in some versions) of the currentStyle object, but without IE, or Windows, I'm unable to offer insight. There is a link in the references, below, that will take you to Microsoft's documentation.
References:
currentStyle object.
HTMLElement.style.
Window.getComputedStyle().
You need to look at element.style, not element.id.style. The element's ID does not have a style.
alert(document.activeElement.style.border);
Scenario
I have created a page where the client can build their own page, calendars, widgets, articles etc. I have created a second Dynamic builder page where they can build their own newsletters.
Problem
All my css is referenced with classes, because mailers are very limited I have to add all styles inline.
Question
Is there a script I can run to grab all referenced styles via class, and add it to the relevant elements/tags inline-styles?
Example [simple]
<p class='txtBlack'>Hello World</p>
Converts to
<p class='txtBlack' style='color:#000;'>Hello World</p>
Hope this is clear enough to understand.
I'd use element.currentStyle and window.getComputedStyle() for each element, then 'manually' read what I want and overwrite what I'm sure that doesn't work in mail apps.
I made example here: http://jsfiddle.net/Vmc7L/
Another way, is to read rules form style sheets and then apply them to inline style. But what if u got selectors like .myClass:firstChild>.anotherClass? :D Maybe jquery can help.
There're methods you need: http://www.quirksmode.org/dom/w3c_css.html
This so answer explains how:
Can I access the value of invalid/custom CSS properties from JavaScript?
CSSStyleDeclaration (https://developer.mozilla.org/en/DOM/CSSStyleDeclaration)
div {
width: 100px;
}
style:CSSStyleDeclaration object contains cssText:
cssText: "width: 100px"
CSSStyleDeclaration specification: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
To get all elements with class names use:
jQuery("[class]")
Here is the solution u can use the "getElementsByClassName" javascript function to collect the elements with the class names specified. But remember this doesnot work IE browsers. So for IE u have to have your own function. Hope this helps u.