In Polymer 0.5, one could use the tokenList filter with expressions on an elements class attribute to apply classes conditionally based on object values. What is the v1.0 replacement or equivalent technique? I can't find anything on the subject beyond handling it entirely in code.
Polymer 1.0 made quite a few cuts in favor of performance gains, expressions being one of those.
Using the example from the 0.5 documentation:
<div class="{{ {active: user.selected, big: user.type == 'super'} | tokenList}}">
You could re-write for 1.0 like so:
<div class$="{{getClassList(user.selected, user.type)}}">
Then in your element's js:
getClassList: function(selected, type) {
var classList = '';
if (selected) classList += ' active';
if (type == 'super') classList += 'big';
return classList;
}
Make sure that any properties that are subject to change (and that the resulting value depends on) are passed as parameters to the function. If these properties are updated, polymer will re-compute the value. Also make sure that each property passed to the function are initialized in some way -- Polymer won't compute the property if any arguments are undefined.
Another thing to be aware of is that any occurrence of {{}} must take up the whole attribute or text content, so you can't have things like class="foo {{bar}}". If you need to declaratively add a class name to your element, you could do something like this:
<div class$="{{getClassList('user-item', user.selected, user.type)}}">
Related
I'm working on a JS class that creates several elements and attaches them to the DOM, and I would like for the class to also set the style attribute properties of those elements. I have a function already that does this for other attributes:
function setAttrs(el, attrValArray){
for(var i=0; i<attrValArray.length;i++){
el.setAttribute(attrValArray[i][0],attrValArray[i][1]);
}
}
The attrValArray input is a 2D array that would look something like
attrValArray = [["class","container"],["id","cc1]];
So what I want is to be able to create a similar array for style property pairs such as:
propValArray = [["display","inline-block"],["position","relative"]];
Which I would then pass to a similar setStyles function.
I could use the same setAttribute method, but instead of looping over the array and setting attributes individually I would have to construct a long string and pass the whole thing as the second argument of setAttribute since I am actually setting many properties of only 1 attribute. But I'd like to avoid this because of the fact that it would override any existing inline styles (not that I use inline styles, but for the sake of principle).
The better option is to set the properties of style on the element. I.e.
el.style.<property-to-set> = "property value";
This does not overwrite anything other than the specific property being set. But I don't see any way to select the "property-to-set" using values from a list. Is there an appropriate way around this?
To be clear, I already know that I can simply assign the desired style attributes to the to-be-created element classes/ids, and was actually wondering if that is the technically correct thing to do based on "good practices" and whatnot. That is what I plan on doing if there is no satisfying alternative.
Also, as stated in the question, I'm strictly interested in a pure JS solution, no JQuery or any other such library/framework.
I don't see any way to select the "property-to-set" using values from a list.
Unless I'm misunderstanding the question, you would do it pretty much the exact same way:
function setStyles(el, cssArray){
for(var i=0; i<cssArray.length;i++){
el.style[cssArray[i][0]] = cssArray[i][1];
}
}
Remember with objects, you can access properties using dot notation object.property or bracket notation object['property']. When you use bracket notation, the text inside the brackets is evaluated, just like when looking up indexes in an array (arrays are really just special objects with number properties).
Alternatively, you could build one long string out of all the property/values pairs and use setAttribute or cssText:
function setStyles(el, cssArray){
let cssText = '';
for(var i=0; i<attrValArray.length;i++){
cssText += cssArray[i][0] + ':' + cssArray[i][1] + ';';
}
el.style.cssText = cssText;
// or el.setAttribute('style', cssText);
}
I've got a component called phrase, which is used like this:
<phrase *ngFor="let phrase of phraseList.phrases" [attachedPhrase]="phrase"></phrase>
Let's say now, I get one of these phrase components using jQuery. How do I access attachedPhrase?
[attachedPhrase] is not an attribute but property binding. It is supposed to be available only inside Angular application.
Although it's possible to access it as ng-reflect-* attribute, this can be recommended only for debugging purposes (this is the reason why these attributes are available in debugging mode only).
Considering that phrase is a string and attachedPhrase should be available both as component input and DOM attribute, it should be changed to attribute binding:
<phrase *ngFor="let phrase of phraseList.phrases" attachedPhrase="{{ phrase }}"></phrase>
Since attributes are case insensitive, it will be compiled to
<phrase attachedphrase="..."></phrase>
Property and attribute bindings can be interchangeable, but only if the expression is expected to be interpolated to a string.
Whenever possible, it's always preferable to not rely on DOM selectors and provide $(...) with actual reference to DOM element (nativeElement property of ViewChild or ElementRef element reference).
You can try: [attr.attachedPhrase]="'phase'".
Detailed info you can check out this site about Angular 5 directive.
Is there a way, using JavaScript, that I could deduce whether an element has a specific CSS definition applied to it, that wasn't inherited from the browser's stylesheet?
I need to know whether a given element has had its position definition explicitly defined (explicitly being a definition not from the browser's stylesheet).
What'd initially come to mind is executing an algorithm to compare said element's style definition with the default style definition for the same element. However, the scenario where the same definition as the default definition (defined in default stylesheet) is also important.
Say the element in question is a div. The default positioning, across browsers, is static. If it's value is absolute or relative, the answer's easy. But, if it's static, there's no "easy" way, that I know of, to determine if it was a 'user supplied styling' (stylesheet or inline-style).
Thinking of something akin to object.hasOwnProperty. However, in this case, it'd be called on the classlist and a property will be passed as a parameter; with a boolean return value indicating if the property has been set by a 'user defined definition'?
All elements have a standard position property of static. You can use that knowledge in conjunction with window.getComputedStyle to deduce whether the element has a custom defined position:
var elem = document.querySelector('....');
if (window.getComputedStyle(elem, null) !== 'static') {
// The element has custom position defined in either .style in via a css rule
}
This has the obvious crux that it does not catch the case when you specifically an element's position to be static. To do that efficiently, you'd have to see which CSS styles apply for your specific element and see if any of them is 'position'. You can use something like: https://github.com/Box9/jss to get that. (there used to be window.getMatchedCSSRules, but that's deprecated)
If you don't want to use a library to achieve this, you can manually parse all the document.styleSheets for rules and see if the selectorText matches your element.
This is just a method I wrote up in 5 minutes and works pretty fine (though take care, this is not the most performant method in the world):
function getStylesForElement (elem) {
var result = {};
[].slice.call(document.styleSheets).forEach(function (stylesheet) {
// some stylesheets don't have rules
if (!stylesheet.rules) return;
[].slice.call(stylesheet.rules).forEach(function (rule) {
// account for multiple rules split by a comma
rule.selectorText.split(',').forEach(function (selector) {
if (elem.matches(selector)) {
for (var index=0; index < rule.style.length; ++index) {
var prop = rule.style[index];
result[prop] = rule.style[prop];
}
}
});
});
});
return result;
}
In order to find children objects of a certain class name, I had to create my own helper function
findChildrenByTagName = function(obj, name){
var ret = [];
for (var k in obj.children){
if (obj.children[k].className === name){
ret.push(obj.children[k]);
}
}
return ret;
}
An example of how it works is
var li = document.createElement('li');
var input = document.createElement('input');
input.setAttribute('class','answer_input');
li.appendChild(input);
console.log(findChildrenByTagName(li,"answer_input"));
However when I replace className above by class in the function above, the code doesn't work. So I am naturally wondering what the difference is between class and className. A quick search on google doesn't reveal anything.
Also what's the best way to return a list of children of a particular class name for a generic object? If such doesn't exist, is there a way to add a method to all objects so that I can call
li.findChildrenByTagName("answer_input");
instead of the global function above?
Let's break this into answerable parts:
Question 1:
What is the difference between class and classname in javascript?
Your title question.
Answer 1:
Class is an attribute in an html element <span class='classy'></span>
While, on the other hand, .className is a property that can by called on an element to get/set its class.
var element = document.createElement('span');
element.className = 'classy'
// element is <span class='classy'></span>
Setting the class can also be accomplished with .getAttribute('class') and .setAttribute('class', 'classy'). We change manipulate classes so often, however, that it merited its own .className method.
Question 2:
However when I replace className above by class in the function above, the code doesn't work. So I am naturally wondering what the difference is between class and className.
Answer 2: element.class is not a property.
Class may be an attribute of an element, but you can't call it like el.class. The way you do it is by el.className, like you already figured out. If you look at the MDN for Element, you'll see that elements have many properties and methods, but .class isn't one.
Question 3:
Also what's the best way to return a list of children of a particular class name for a generic object?
Answer 3: Use .getElementsByClassName
Rather than using a purpose-made function, people frequently "chain" methods one-after-another to achieve your desired effect.
Based on your needs, I think you're asking for the .getElementsByClassName method. Here are the full docs and an excerpt:
The Element.getElementsByClassName() method returns returns a live HTMLCollection [array] containing all child elements which have all of the given class names.
To reuse the example from your answer:
var li = document.createElement('li');
var input = document.createElement('input');
input.setAttribute('class','answer_input');
li.appendChild(input);
console.log(li.getElementsByClassName("answer_input")[0]);
// would return the <input class='answer_input'> as the first element of the HTML array
Don't get confused between the reserved word 'class' and 'className' attribute of DOM element.
According to MDN:
The name className is used for this property instead of class because of conflicts with the "class" keyword in many languages which are used to manipulate the DOM.
EDIT:
The 'class' word used on js 1.0 but in js 2.0 was merged with ECMAScript 4 which was rather unpopular and depreciated. but it is still a reserved word.
The general concept is that class is an object and className is "one" of its properties.Refer the links on how to use the class attribute for manipulation.Kindly correct me if my perception/understanding is wrong.
https://www.w3schools.com/jsref/prop_html_classname.asp
https://www.w3schools.com/jsref/prop_element_classlist.asp
It appears that sometimes object.setAttribute(attrib,value) isn't equivalent to object.attrib=value in javascript?
I've got the following code, which works fine:
var lastMonthBn = document.createElement('input');
lastMonthBn.value='<'; // This works fine
lastMonthBn.type='button'; // This works fine
But the following code doesn't:
var div = document.createElement('div');
div.class = 'datepickerdropdown'; // No luck here!
So i need to use the following:
div.setAttribute('class','datepickerdropdown');
My question is, why? From reading this, I thought that object.setAttribute(blah,value) was the same as object.blah=value??
Properties and Attributes aren't really the same, however the DOM exposes standard attributes through properties.
The problem you're facing specifically with the class attribute is that class is a future reserved word.
In some implementations the use of a future reserved word can cause a SyntaxError exception.
For that reason, the HTMLElement DOM interface provides a way to access the class attribute, through the className property:
var div = document.createElement('div');
div.className = 'datepickerdropdown';
Remember, attributes aren't the same as properties, for example:
Immagine a DOM element that looks like this:
<div></div>
If you add a custom attribute to it, e.g.:
myDiv.setAttribute('attr', 'test');
An attribute will be added to the element:
<div attr="test"></div>
Accessing attr as a property on the div element, will simply give you undefined (since is not a property).
myDiv.foo; // undefined
If you bind a property to an element, e.g.:
myDiv.prop = "test";
The getAttribute method will not be able to find it, (since is not an attribute):
myDiv.getAttribute('test'); // null
Note: IE wrongly messes up attributes and properties. :(
As I've said before, the DOM exposes standard attributes as properties, but there are some exceptions that you'll have to know:
The class attribute, is accessible through the className property (the problem you have).
The for attribute of LABEL elements, is accessible through the htmlFor property (collides with the for statement).
Attributes are case-insensitive, but the language bindings for JavaScript properties are not, so the convention is to use the names is camelCase to access attributes through properties, for example the ones formed by two words, e.g. cellSpacing, colSpan, rowSpan, tabIndex, maxLength, readOnly frameBorder, useMap.
It should be noted that browsers like Safari will NOT run JavaScript if keywords like "class" or "int" are present.
So it's a cross-browser support sort of thing. "class" is present in JS2.0 [I believe a package system is available there too]
...
I should also note that in IE, setAttribute [for non-class things, since setAttribute should be use-able for other members such as "style"] can be glitchy.