Is there a way to assign attributes in a more compact manner
I dont really want to use setAttribute as it seems to be buggy in ie8
This list is for all attributes so its quite long
else if(a=="textalign")
{
e.style.textAlign="";
e.align=v
}
if(a=="textalign")
{
e.style.textAlign="";
e.align=v
}
I don't know why you are trying to set alignment via an HTML attribute rather than just using the CSS... this is much less reliable as there are many elements which have no align attribute. HTML align is also deprecated and should be avoided in general.
You don't say what the “other attributes” are that you might want to set. If you are talking specifically about HTML attribute properties it's easy to set them by a name in a string:
e[a]= v;
But then you need a to be the HTML attribute property name, which would be ‘align’ not ‘textalign’. It wouldn't do anything special to try to workaround CSS overrides like textAlign, because there is no automated way to do that, and the interaction between the deprecated HTML styling attributes and CSS is ill-defined. Stick to attributes or CSS (CSS is highly preferable); don't use both.
If you are talking about setting any CSS style property, as I might guess from the name being ‘textalign’, that's done similarly:
e.style[a]= v;
But then, again, you'd want to be using the exact style property name ‘textAlign’ not ‘textalign’.
If you want to set CSS style properties by their CSS name, like ‘text-align’, you could transform that to the DOM name automatically:
// convert foo-bar-baz to fooBarBaz
//
var doma= a.replace(/-([a-z])/g, function(m, g) {
return g.toUpperCase();
});
e.style[a]= v;
If you really do need to use case-lossy names like ‘textalign’ you'd have to use a lookup of all property names you wanted to use to get the case back:
var propernames= ['textAlign', 'borderColor', 'paddingTop']; // etc
for (var i= propernames.length; i-->0;)
if (propernames[i].toLowerCase()===a)
a= propernames[i];
e.style[a]= v;
Forget setAttribute. It has nothing to do with style properties (it's a bug in IE6-7 that it even works on styles there), and you shouldn't use it on elements either for HTML documents, as there are other IE6-7 bugs to contend with there. Stick to the ‘DOM Level 2 HTML’ direct property access stuff, which is more reliable and easier to read.
Use a class instead of giving all the attribute values.
.testClass
{
// set all attribute values here
}
e.className = "test";
See
element.className
Use some framework such as JQuery, it takes care of all of your browser incompatibility issues. In JQuery you use the .css('attributeName', 'value')method.
jQuery would make that easy with .attr({attr1: val, attr2: val}) etc. It would also shield you from many cross-browser compatibility bugs.
Related
We have a corporate content management system that allows for rich text editing/html markup, but does not allow for head elements or style sheets to be uploaded, attached, or used in any way. It provides some rich text editing controls and also access to the source html, but just for the html fragment -- there is no head, no body. We also have no access the whole system that presents these bits of markup on the page. The only way to style the content is through inline style attributes on the elements. It is best, it isn't pretty, but that is what we have and I'm trying to make the best of a bad situation.
We also have high standards for visual presentation and would like to be able to quickly produce and modify/update content and keep it looking nice. It is difficult to correctly apply formatting using the system. For anybody who has tried to markup anything more than a paragraph or two with an RTE, you probably know what I mean. It seems like we should have a different system, but has anybody worked for a large company before? Just sayin.
We do have access to another location where we could "author" and "store" actual styled content and then "compile it" for copypasta into the other system. In other words, we could author/design using css and best practices and then we could run some code that could convert those element, class, and id formatting into inline styles.
I did my research and found this thread which also lead me to this code.
These both are very helpful in exploring solutions, but I've run into an issue. These solutions use the javascript getComputedStyle() method. There are some other options for properties to only look at other properties or to be recursive on the children of the element provide, but basically it boils down to this. (Since getComputeStyle returns an object and not an array, there is also a prototype/polyfill to allow iterating over an object with forEach, but none of that is part of the issue I'm facing.)
const computedStyle = getComputedStyle(element);
computedStyle.forEach(property => {
element.style[property] = computedStyle.getPropertyValue(property);
});
This works well for css attributes like font-size:24px or margin:0 15px. The issue I'm running into are when I'm using units other than px. For example, if I'm trying to make something that has width:50%. getComputedStyle() converts the 50% to the actual number of pixels that 50% is currently using.
In the notes section of the MDN web docs I see that this is expected behavior. Although I'm not quite clear on what that last line means.
...An example difference between pre- and post-layout values includes the
resolution of percentages for width or height, as those will be
replaced by their pixel equivalent only for used values.
So what I'm trying to do is convert something like this
.container{width:50%;}
<div class="container">
into something like this
<div class="container" style="width:50%">
Does anyone know of a way to complete this type of transformation?
PS: If it matters we'll be using the more basic attributes in our css -- no transitions, grid, prefixing, etc. We still need to support IE 11 -- if that tells you anything. We won't need to account for every edge case or browser. Just some basic stuff so that all our H1 look the same.
Couldn't find any way to do this using the built in getComputedStyle(). It also returned too many properties that I wasn't interested in. So I came up with a different approach. Basically to use the same function to loop through an element (and maybe all its children elements) and the use Element.matches() to get all the css rules that apply to the element and apply the properties as they were specified in the stylesheet.
I modified this answer a bit to get the rules from the stylesheet.
Has the added benefit that we can pull either from all the document stylesheets or just from a specific one that is needed for preparing the code to go into our content management systems's rich text editor.
function applyInline(element, recursive = true) {
if (!element) {
throw new Error("No element specified.");
}
const matches = matchRules(element);
// we need to preserve any pre-existing inline styles.
var srcRules = document.createElement(element.tagName).style;
srcRules.cssText = element.style.cssText;
matches.forEach(rule => {
for (var prop of rule.style) {
let val = srcRules.getPropertyValue(prop) || rule.style.getPropertyValue(prop);
let priority = rule.style.getPropertyPriority(prop);
element.style.setProperty(prop,val,priority);
}
});
if (recursive) {
element.children.forEach(child => {
applyInline(child, recursive);
});
}
}
function matchRules(el, sheets) {
sheets = sheets || document.styleSheets;
var ret = [];
for (var i in sheets) {
if (sheets.hasOwnProperty(i)) {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
if (el.matches(rules[r].selectorText)) {
ret.push(rules[r]);
}
}
}
}
return ret;
}
Modernizr provides a testAllProps() method which conveniently tests all the vendor prefixed styles of the one given to see if the style is supported by the currently running browser.
However I have no come to a point where I need to actually assign these properties from javascript because of various reasons that boil down to it being too cumbersome to conditionally link CSS files.
So for instance I could build an array and a routine which assigns each vendor specific style to the style of my target element:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration'].map(function(s){ element.style.s = "style_setting"; });
Well, this will probably generate a bunch of errors because I will try to assign "style_setting" to 4 or 5 undefined values.
Does anybody know anything to make this a bit less painful?
Probably best to use an existing library that knows all about this stuff:
Prefix Free will let you assign styles from CSS without vendor-prefixing. There is also a jQuery Plugin for it that will allow you to do the same from JavaScript.
Before setting the value, check whether the property is undefined:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration']
.map(function(s) {
if (element.style[s] != undefined) element.style[s] = "style_setting";
});
In DOM, is it OK to refer to an element's attributes like this:
var universe = document.getElementById('universe');
universe.origin = 'big_bang';
universe.creator = null;
universe.style.deterministic = true;
? My deep respect for objects and their privacy, and my sense that things might go terribly wrong if I am not careful, makes me want to do everything more like this:
var universe = document.getElementById('universe');
if(universe.hasAttribute('origin')) then universe.origin = 'big_bang';
etc...
Is it really necessary to use those accessor methods? Of course it may be more or less necessary depending on how certain I am that the elements I am manipulating will have the attributes I expect them to, but in general do the DOM guys consider it OK to use .notation rather than getters and setters?
Thanks!
For XML documents, you must use getAttribute/setAttribute/removeAttribute etc. There is no mapping from JavaScript properties to DOM attributes.
For HTML documents, you can use getAttribute et al to access attributes, but it's best not to because IE6-7 has difficulties with it. The DOM Level 2 HTML properties are not only more reliable, but also easier to read.
It's unclear whether you're using XML or HTML documents here. Clearly origin is not an HTML attribute; ‘custom’ elements and attributes like this should not be included in HTML documents. But it's unclear what universe.style.deterministic refers to; you wouldn't get a CSS style lookup mapped without an HTML style attribute.
Yes, it's fine ;-)
If there's an attribute in the DOM, you can set it or get it, directly.
No private or read-only elements or anything. By the way, JavaScript doesn't have a 'then' keyword.
Due to cross browser issues I always use getAttribute and setAttribute:
if(!universe.getAttribute('origin'))
{
universe.setAttribute('origin', 'big_bang');
}
I don't recall the specifics but I have had problems with the property style universe.origin and dynamically created DOM elements.
No, it's not fine to do so. Most properties of DOM objects can be overwritten. You won't ruin the browser's behavior, since it doesn't use the DOM API. But you will ruin your JS scripts if they attempt to use the overwritten property in its original meaning.
My own way of doing things, when I have several attributes to attach to an object (as opposed to a single flag or link), is to create a custom object and then link it from the DOM element:
var Universe = {
origin: "big_bang",
creator: null,
style: { deterministic: true }
};
document.getElementById('universe')._universe = Universe;
I'm writing a simple jQuery that to change the font size of an element by a certain percentage. The problem I'm having is that when I get the size with jQuery's $('#el').css('font-size') it always returns a pixel value, even when set with em's. When I use Javscript's el.style.font-size property, it won't return a value until one has been explicitly set by the same property.
Is there some way I can get the original CSS set font-size value with Javascript? How cross-browser friendly is your method?
Thanks in advance!
Edit: I should note that all the tested browsers (See comment below) allow me to set the the text-size using an 'em' value using the two methods mentioned above, at which point the jQuery .css() returns an equivalent 'px' value and the Javascript .style.fontSize method returns the correct 'em' value. Perhaps the best way to do this would be to initialize the elements on page load, giving them an em value right away.
All of your target browsers with the exception of IE will report to you the "Computed Style" of the element. In your case you don't want to know what the computed px size is for font-size, but rather you want the value set in your stylesheet(s).
Only IE can get this right with its currentStyle feature. Unfortunately jQuery in this case works against you and even gets IE to report the computed size in px (it uses this hack by Dean Edwards to do so, you can check the source yourself).
So in a nutshell, what you want is impossible cross-browser. Only IE can do it (provided you bypass jQuery to access the property). Your solution of setting the value inline (as opposed to in a stylesheet) is the best you can do, as then the browser does not need to compute anything.
In Chrome:
var rules = window.getMatchedCSSRules(document.getElementById('target'))
returns a CSSRuleList object. Need to do a bit more experimentation with this, but it looks like if m < n then the CSSStyleRule at rules[n] overrides the one at rules[m]. So:
for(var i = rules.length - 1; i >= 0; --i) {
if(rules[i].style.fontSize) {
return /.*(em|ex|ch|rem|vh|vw|vmin|vmax|px|in|mm|cm|pt|pc|%)$/.exec(rules[i].style.fontSize)[1];
}
}
In Firefox, maybe use sheets = document.styleSheets to get all your stylesheets as a StyleSheetList, then iterate over each CSSStyleSheet sheet in sheets. Iterate through the CSSStyleRules in sheet (ignoring the CSSImportRules) and test each rule against the target element via document.getElementById('target').querySelector(rule.selectorText). Then apply the same regexp as above..... but that's all just a guess, I haven't tested it out or anything....
This jQuery plugin looks like it will do the trick:
Update: jQuery Plugin for Retaining Scalable Interfaces with Pixel-to-Em Conversion
Github: jQuery-Pixel-Em-Converter
i had this problem once. i use this function to get the int value of a css attribute, if there is one.
function PBMtoInt(str)
{
return parseInt(str.replace(/([^0-9\.\-])+/,"")!=""?str.replace(/([^0-9\.\-])+/,""):"0");
}
I'm trying to find a way that will add / update attribute using JavaScript. I know I can do it with setAttribute() function but that doesn't work in IE.
You can read here about the behaviour of attributes in many different browsers, including IE.
element.setAttribute() should do the trick, even in IE. Did you try it? If it doesn't work, then maybe
element.attributeName = 'value' might work.
What seems easy is actually tricky if you want to be completely compatible.
var e = document.createElement('div');
Let's say you have an id of 'div1' to add.
e['id'] = 'div1';
e.id = 'div1';
e.attributes['id'] = 'div1';
e.createAttribute('id','div1')
These will all work except the last in IE 5.5 (which is ancient history at this point but still is XP's default with no updates).
But there are contingencies, of course.
Will not work in IE prior to 8:e.attributes['style']
Will not error but won't actually set the class, it must be className:e['class'] .
However, if you're using attributes then this WILL work:e.attributes['class']
In summary, think of attributes as literal and object-oriented.
In literal, you just want it to spit out x='y' and not think about it. This is what attributes, setAttribute, createAttribute is for (except for IE's style exception). But because these are really objects things can get confused.
Since you are going to the trouble of properly creating a DOM element instead of jQuery innerHTML slop, I would treat it like one and stick with the e.className = 'fooClass' and e.id = 'fooID'. This is a design preference, but in this instance trying to treat is as anything other than an object works against you.
It will never backfire on you like the other methods might, just be aware of class being className and style being an object so it's style.width not style="width:50px". Also remember tagName but this is already set by createElement so you shouldn't need to worry about it.
This was longer than I wanted, but CSS manipulation in JS is tricky business.
Obligatory jQuery solution. Finds and sets the title attribute to foo. Note this selects a single element since I'm doing it by id, but you could easily set the same attribute on a collection by changing the selector.
$('#element').attr( 'title', 'foo' );
What do you want to do with the attribute? Is it an html attribute or something of your own?
Most of the time you can simply address it as a property: want to set a title on an element? element.title = "foo" will do it.
For your own custom JS attributes the DOM is naturally extensible (aka expando=true), the simple upshot of which is that you can do element.myCustomFlag = foo and subsequently read it without issue.