I am creating this JS function that creates an element
The function takes two parameters strName and objAttributes
function createElement( strName, objAttributes )
{
var elem = document.createElement(strName);
for ( var i in objAttributes )
elem.setAttribute(i, objAttributes[i]);
return elem;
}
This works fine in Fx, but not in MSIE
I know that the setAttibute method is buggy and the proposed workaround is
elem.attr = 'val';
But right now I have no idea how to write this inside my loop.
I have tried both elem.style and elem['style'] but none of them works.
Can anyone give me some advice,
thanks in advance
t
Use elem[i].
function createElement( strName, objAttributes )
{
var elem = document.createElement(strName);
for ( var i in objAttributes )
elem[i] = objAttributes[i];
return elem;
}
You can't just swap setting properties and setAttribute.
You have to be careful with setting properties on an element in place of using setAttribute.
Style properties and event handlers need to be carefully written, and those attributes that used to be minimized in html (disabled, multiple, readonly) have browser specific valid values.
Also, if you set element.class="mynewclass", you'll get an error, because class is a reserved javascript word, though it is perfectly safe to use it as a string in a setAttribute assignment. THe property name is '.className', and the proper name for a label's 'for' attribute is 'htmlFor'.
Let jQuery handle the cross-browser nonsense...
$(elem).attr(i, objAttributes[i]);
Related
I have a problem to get DOM element's attributes in JS. Here is code:
return elem.getAttribute(attr) || elem[attr] || "";
elem.getAttribute(attr) is used to get attributes like name or id, elem[attr] is used to get attribute like tagName. It works fine, until style came out.
In my case, I want to have "" when style attribute not set. But with above code, it will try elem[attr] when elem.getAttribute(attr) returns null. So if style is not set, I get all browser supported styles instead of "".
How to deal with this problem? Is there any better way than enum attributes?
Edit:
I want to write a general function to get element's attributes (such as name, style) or properties(such as tagName).
The main difference is elem.getAttribute(attr) try to get an attribute in the tag element, but elem[attr] try to get a property from an object, is important to know that elem inherits all properties from the Element Object, this properties are declared and in some cases defined, one of this properties is style.
In the particular case of the style property, by default this has been defined with an CSSStyleDeclaration, that's the reason you get attributes of style.
If you want only check if the attribute is in the tag, I suggest you only use this code:
return elem.getAttribute(attr) || "";
This is a code I use on my applications, so I'll just copy & paste it:
Object.defineProperty( Element.prototype, "hashAttr", { get: function(){
/* Bekim Bacaj 2008 */
var hash=[], i = 0, x = this.attributes;
while( x[i] ){hash[x[i].name] = x[i++].value};
return hash;
}})
;
which is, to my knowledge, the fastest possible.
This is a sample return from an element that has no inline or JavaScript assigned styles on its tag.:
>> media.hashAttr
{
width : "100%",
height : "100%",
id : "media",
src : "http://*****.***/stream/*****.mp4",
autoplay : "false",
poster : "http://******.***/thumb/*****.jpg",
type : "video/mp4"
}
Notice that, therefore, no offline style-attribute is present in the property list.
I have an answer. It handles tagName specially. It's not great. But it can get the job done.
var value = elem.getAttribute(attr);
if (!value) {
if (attr == "tagName") {
value = elem["tagName"] || "";
} else {
value = "";
}
}
return value;
I wanna make a varible shortcut $$() so that i can use shortcut like $() [jquery] to save code in my project(ALL MY CODE IS PURE JAVASCRIPT).
when i put the string of id or class, it works all right, but when i put the tagName, it shows Cannot read property 'style' of undefined, it seems that the code is right,help,thanks
One more, is that way to defined a shortcut variable $$() to use in pure javascript environment right way? or is there any best practice to define a global variable like this?
window.onload = function(){
function $$(ele){
var pattern1 = /#/g;
var pattern2 = /\./g;
var pattern3 = /!/g;
var matches = ele.match(/[^#\.!]/g);//array
var elementS = matches.join("");
//alert(matches+elementS);
// console.log(document.getElementsByTagName(elementS));
var spaceExist = /\s/.test(elementS)
if(pattern1.test(ele)){
return document.getElementById(elementS);
}else if(pattern2.test(ele)){
//console.log(elementS);
return document.getElementsByClassName(elementS);
}else if(pattern3.test(ele)){
alert('hi');
console.log(elementS);
return document.getElementsByTagName(elementS);
}else if(spaceExist){
return document.querySelectorAll(elementS);
}
}
$$('#hme').style.backgroundColor = 'red';
$$('.myp')[0].style.backgroundColor = 'green';
$$('!h2')[0].style.display = 'none';//this not work,shows Cannot read property 'setAttribute' of undefined
}
<h1 id="hme">hi,friend</h1>
<p class="myp">mmdfdfd</p>
<h2>hhhhhh</h2>
Have you stepped through your code? Look at pattern #2:
var pattern2 = /./g;
That pattern will match any character at all given that's what the period represents in regular expressions - ref: http://www.regular-expressions.info/dot.html.
Therefore, this conditional is satisfied and returns its result:
else if(pattern2.test(ele)){
return document.getElementsByClassName(elementS);
}
Given there appears to be no element with a class name of h2 (which is the value of elementS), the return value is undefined.
Given that undefined has no properties, interrogating for the style property will produce the error you are seeing.
My advise is use one shortcut since you already using querySelectorAll:
window.$ = document.querySelectorAll.bind(document)
or if you rather need first element
window.$ = document.querySelector.bind(document)
this way you'll be able to do everything you are doing with normal css selectors and not obfuscated !tag for just tag
If speed actually matters, you will save some ticks by just having two aliases:
window.$ = document.querySelector.bind(document)
window.$el = document.getElementById.bind(document)
and calling $el when you need it specifically, instead of trying to make method polymorph.
Mister Epic's answer spots the main issue. Your h2 call is getting caught in that if statement, and that's why your error is happening. You need to make sure it doesn't get caught there, either by creating another pattern, or specifying in your second if statement that your 'ele' doesn't contain an '!'.
After that, in your third if statement:
else if(pattern3.test(ele)){
alert(hi); <---
console.log(elementS);
return document.getElementsByTagName(elementS);
The problem with this is you're going to alert(hi), but hi isn't defined. Make sure you wrap it in quotes.
Should be looking good after that.
I have a existing application which uses javascript and properties like notNull, isDate etc defined within the elements in html elements like input, select, etc
For example:
<input type = 'text' notNull class='mandatoryField' name = 'abc' id='abc' isDate/>
And the javascript checks for the properties with a hasProp method, placing the code below and corresponding warning messages are displayed:
function hasProp(thisField, thisProp) {
for ( var prop in thisField) {
if (prop == thisProp)
return true;
}
return false;
}
My issue here is with using different browsers - IE, Chrome and Firefox
This particular methods are all ok for Internet Explorer. when it comes to chrome and firefox, the notNull, isDate are treated as attributes rather than properties and the above hasProp method always returns false.
I did go through many questions available here, but couldn't find any way to access both properties and attributes in a single way - I would prefer jQuery to be used, since we will be migrating to jQuery eventually.
Any pointers to this will be really helpful.
Thanks,
Reema
I think the way you use the attribute and property aren't 100% accurate, properties (.prop()) in the jQuery context are basically the value of the attribute in memory, where as .attr() reflects the value in the markup. This is only the case for HTML attributes that are "built-in".
So in your example, you're dealing with attributes all the way, just some don't happen to have any value.
The best way of detecting the presence of an attribute, cross browser using jQuery:
$('#myElement').is('[attrName]') === true
So, in your case:
$('#abc').is('[isDate]') === true
See this JS-Fiddle
As you want a jQuery solution, you can use both jQuery.attr() and jQuery.prop() methods to solve your problem.
I would prefer an pure Javascript approach:
var attributes = ['notNull', 'isDate'],
checkForAttribute = function(elem){
for(var i = 0, c = attributes.length ; i < c ; i++){
if(elem.getAttribute(attributes[i]) !== null){
console.log("attribute " + attributes[i] + " found");
}else{
console.log("attribute " + attributes[i] + " not found");
}
}
}
See an working example here.
Here is some more information on the getAttribute() method.
I'm using custom tags to define sections in an application, so I have something like this:
<mysection>
<form>
<input name="myfield">
</form>
</mysection>
I'm using the following and able to get the tag (printed to console, everything is groovy)
var parent = document.getElementsByTagName('mysection');
The issue I'm having is finding the child field by name:
var myfield = parent.getElementsByName("myfield");
...as I don't want to pick up on any other 'sections' that might have an input with the name 'myfield'.
EDIT:
var parent = document.getElementsByTagName('mysection')[0];
was suggested and returns to console the section contents, however, getElementsByName throws an error:
Uncaught TypeError: Object #<NodeList> has no method 'getElementsByName'
Using getElementsByTagName() and getElementsByName() will return a NodeList, you need to get the first element of the list like this:
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.getElementsByName("myfield")[0];
Edit
You were correct, getElementsByName is not valid for an element. I am unsure how to localize the functionality of it as you are trying to do. It seems that it will only work for document. You may have to write your own implementation of getElementsByName if you want to use it in a localized scope.
Second Edit
To be nice, I made that implementation for you :D Here it is in all its "glory".
Element.prototype.getElementsByName = function (arg) {
var returnList = [];
(function BuildReturn(startPoint) {
for (var child in startPoint) {
if (startPoint[child].nodeType != 1) continue; //not an element
if (startPoint[child].getAttribute("name") == arg) returnList.push(startPoint[child]);
if (startPoint[child].childNodes.length > 0) {
BuildReturn(startPoint[child].childNodes);
}
}
})(this.childNodes);
return returnList;
};
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.getElementsByName("myfield")[0];
Small fix
I was incorrectly passing the element and not its children into the recursion. The code above has been edited with the proper argument passed now. See working fiddle: http://jsfiddle.net/js6NP/5/
I actually found a much more simple way to handle this:
document.querySelectorAll('mysection [name="myfield"]');
Here you can see an example where it only modifies the field inside the section specified: http://jsfiddle.net/fluidbyte/kph6H/
qSA supports modern browsers and is compatible down to IE8, Here's a polyfill to support back to IE7: https://gist.github.com/2724353
getElementsByName won't work on a DOM element reference. Use querySelector or querySelectorAll instead. In example:
var parent = document.getElementsByTagName('mysection')[0];
var myfield = parent.querySelector("[name='myfield']");
Just use an ID instead:
<mysection>
<form>
<input name="myfield" id="fieldName">
</form>
</mysection>
var myfield = document.getElementById("fieldName");
ID's are supposed to be unique on a page. So you shouldn't have trouble accessing the right element.
If you really have to use name/tagname, getElementsByTagName and getElementsByName both always return a array (A empty one if no element was found). you can access the right element, just like you'd access elements in arrays:
document.getElementsByTagName('mysection')[0]; For the first element with tagname mysection.
I'd like to make a function to the effect of:
function supportsElem(tagName) {
// returns boolean
}
where:
supportsElem("div") // true
supportsElem("randomtext") // false
What the easiest way to do that?
Just a guess, cause i've never had to care about this. But it'd seem to me you could create the element and then check to make sure it acts like it should. For example, that it has certain properties, or that its constructor is right (or at least, isn't the same as a generic unsupported element would have).
An example of the constructor check (untested):
// pick a name that'll never be an element
var generic_element = document.createElement('randomtext');
var tagName_to_check = document.createElement('div');
if (tagName_to_check.constructor === generic_element.constructor) {
// the browser treats the node as a generic element, rather than
// (eg) a DivElement
// so it's probably unsupported
}
Try this:
function testTag(tagname) {
return document.createElement(tagname) instanceof HTMLUnknownElement;
}
I don't know what kind of browser support this (HTMLUnknownElement) will have though.
This is more effort than it's worth.
Just keep a dictionary of all the valid tags (easy to find online). Basically an array of strings
then its just
var dictionary = ["div", "a", "input", "span", ..., "td"];
var myTag = "div";
dictionary.indexOf(myTag); // if this doesn't return -1, then the tag is valid
You could simply create the element, then test it against a method specific to this element.