Is it safe to use removeAttribute instead of removeAttributeNS? - javascript

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.

Related

Why should HTML DOM properties be reflected into HTML DOM attributes as well?

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.

How to access .style on namespaced elements in XHTML

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")

How do I get reference to the ::before or ::after node in JS?

As far as I know, standard JavaScript has no way to get at the ::before or ::after pseudo-elements. Element.children doesn't let you get to it.
I know there has to be a way, at least in Chrome-privileged Firefox add-on code, since it lists every ::before element in the page (and apparently getComputedStyle() works on it too, as you can list all styles of it in inspector, which is written in JavaScript).
Where is this API documented, and is it something that's different and privileged-only in say Firefox and Chrome browser, or something that is on track to be standard soon?
The CSS generated content is not part of the DOM, and you wouldn't be able to do much with the ::before/::after pseudo-elements, even if you get at them. The only use-cases I can think of are:
Access the CSS computed values on the pseudo-elements. window.getComputedStyle() supports this via an optional 2nd parameter.
Enumerate the generated content. You can accomplish this:
by using a browser-specific API. In Firefox, the DevTools inspector uses a special interface - inIDeepTreeWalker.
or by walking the DOM and checking (for each element) if it has content in its computed style for :before / :after. For example:
window.getComputedStyle(elt, ':before').content
Get the "live" value of a counter defined in CSS, like in How to access CSS generated content with JavaScript - see that question for details.
At least to me, your question is unclear as to exactly what you are attempting to do, or get.
The most direct equivalent to ::before and ::after:
If you are wanting to actually insert content, which is what the ::before and ::after CSS selectors do, then the most direct equivalent is Element.insertAdjacentHTML(position, text). In that case:
The equivalent of ::before would be:
Element.insertAdjacentHTML("beforebegin", "<p>Additional HTML content before element.</p>");
The equivalent of ::after would be:
Element.insertAdjacentHTML("afterend", "<p>Additional HTML content after element.</p>");
Element.insertAdjacentHTML() also has options of afterbegin and beforeend which insert the HTML text just after the beginning, or just before the end, of the referenced Element.
Alternately:
You could insert nodes using Node.insertBefore(newNode, referenceNode).
For ::before it would be (insert newNode before myNode):
myNode.parentNode.insertBefore(newNode, myNode);
For ::after it would be (insert newNode after myNode):
myNode.parentNode.insertBefore(newNode, myNode.nextSibling);
Obtaining references:
If you are attempting to get a reference to the element that is earlier in the DOM, then it sounds like you are looking for Node.previousSibling. If you are looking for a reference to the element that is later in the DOM, then you are looking for Node.nextSibling.
In DOM walk order:
It is also possible that you are looking for the elements that are just before and just after the reference Node in DOM walk order. However, that is not really what the CSS selectors ::before and ::after do. However, from your mention of Page Inspector, it kind of sounds like this is what you want. If so, then you will can use a TreeWalker to walk the DOM tree.
The following should do what you want (Note: Currently untested, so might be missing something.):
//referenceNode is the node for which we want to find the elements
// before and after in DOM walk order.
//Create the TreeWalker
let treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT,
{acceptNode: function(node) {
return NodeFilter.FILTER_ACCEPT;
}
},
false );
//Point the TreeWalker at the referenceNode.
treeWalker.currentNode = referenceNode;
//Get the node immediately prior to the referenceNode in DOM walk order
let thePreviousNode = treeWalker.previousNode();
//Point the TreeWalker back at the referenceNode.
treeWalker.currentNode = referenceNode;
//Get the node immediately after to the referenceNode in DOM walk order
let theNextNode = treeWalker.nextNode();
As mentioned by Nickolay, if you want the full detail that Page Inspector, or the DOM Inspector (documentation), provides then you will need to use an inIDeepTreeWalker. However, it is unlikely that you want, or need, the detail which using that Firefox specific non-standard interface provides. You only need it if you want to walk through how something like how an XUL <toolbarbutton> is constructed (not the attributes/properties, but the XBL which makes up a XUL elements like a <toolbarbutton>). For the vast majority of what you are potentially thinking about, a standard TreeWalker should be just fine.
With the exception of inIDeepTreeWalker, all of the above are standard parts of JavaScript and do not require elevated privileges (i.e do not require it to be in an add-on).
You can use iniDOMUtils - selectorMatchesElement() function.
You can read more about it here - https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/inIDOMUtils#selectorMatchesElement%28%29

get the style property of document.activeElement.id

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);

JavaScript & copy style

I am copying a table cell with javascript.
It works fine, just that it doesn't copy the style.
I wanted to copy like below, but that didn't work.
newCell.style=oldCell.style;
So I figured that for my text-align, I have to copy it like this:
newCell.style.textAlign=oldCell.style.textAlign;
That worked, but whenever I add a new style item, I have to remember to register it here.
So, my problem now is how can I loop over the style and copy every item in there?
With chrome, I managed to do it like this:
var strAttribute = GetDomNameFromAttributeName(oRow.cells[1].style[0]);
var styletocopy = eval('oRow.cells[1].style.'+strAttribute);
eval("newCell.style."+strAttribute+"='"+styletocopy+"'"); // //newCell.style.textAlign='center';
But that doesn't work with IE. Haven't tested it with FF, but assume chrome compatibiity.
Is there any way to loop over the style elements in IE?
Or is there any better way to copy all style elements?
eval('oRow.cells[1].style.'+strAttribute)
Never use eval like this(*). In JavaScript you can access a property whose name is stored in a string using square brackets. object.plop is the same as object['plop']:
to.style[name]= from.style[name];
(*: never use eval at all if you can help it. There are only a few very specific and rare occasions you need it.)
Is there any way to loop over the style elements
The style object is supposed to support the DOM Level 2 CSS CSSStyleDeclaration interface. You could loop over the rules and apply them to another element like this:
for (var i= from.style.length; i-->0;) {
var name= from.style[i];
to.style.setProperty(name,
from.style.getPropertyValue(name),
priority= from.style.getPropertyPriority(name)
);
}
in IE?
No, IE does not support the whole CSSStyleDeclaration interface and the above won't work. However there is a simpler way not involving looping that will work on IE and the other browsers too:
to.style.cssText= from.style.cssText;
As simple as that! IE doesn't quite preserve the CSS text the way it should, but the difference doesn't matter for simple inline style copying.
However, as Pikrass said (+1), if you are copying a whole element and not just the styles, cloneNode is by far the most elegant way to do that.
You can copy a DOM Element with all its content (including attributes) with .cloneNode(true) :
var clonedTr = document.getElementById('id').cloneNode(true);
Then clonedTr is an exact copy of the tr #id.
The "true" means you want to copy the content of the element.
To copy all style elements from one node to another you can use
newCell.setAttribute('style', oRow.cells[1].getAttribute('style'))

Categories