what is attribute.specified for a DOM element's attributes? - dom

I just stumbled across something like this...
function(element) {
...
var attributes = element.attributes;
for (var index = 0, length = attributes.length; index < length; index++) {
var attribute = attributes[index];
// what is ".specified"?
if (attribute.specified) {
...
}
}
}
I'm looking at the W3C specs for a DOM Element, the Element interface, and I don't see specified anywhere.
http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-745549614
What does attribute.specified mean? What does it represent? Where is it defined in the specs?

<img src="kittens.jpg">. the attribute is src, and its value is kittens.jpg. DOM elements are generic definitions. the actual attributes are specified by the actual language being used, e.g. XML, HTML, etc....
specified = the attribute has an explicit value assigned to it. e.g. the src attribute is specified, because it's been given the value kittens.jpg, but
<input type="checkbox" checked />
the checked attribute is PRESENT, but not SPECIFIED.

I have just found the information that attribute-node's property specified makes sense only for IE 6-7, because in IE 6-7 the attributes returns the collection of all attributes supported by the particular element node. Then you can use specified to find out if the attribute from the collection is attached to the element node. It will return false, if the element node does not have this attribute specified/defined. In modern browsers attributes returns the collection of the attributes attached to the element in fact, it means that every attribute.specified from the collection will return true in modern browsers. For modern browsers the element.hasAttribute(attribute) works the way, that element.attributes[attribute].specified works for IE 6-7.

Different browsers have different implementations of the DOM and note that the implementation is in charge of this attribute, not the user.However, the user can change the default value of the attribute.
As for Chrome 54.0.2840.71 it makes every attribute.specified true, either it is in DOM specification or not, either it has value or not.
for Example,the attribute __test specified is always true in Chrome(version 54).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div __test></div>
<script>
var div=document.querySelector("div");
var attributes = div.attributes;
var attr_test=attributes[0];
attr_test.specified=false;// "specified" is not writable and Setting it silently fails.
console.log(attr_test.specified);
</script>
</body>
</html>
so I agree that "it's on it's way to being depracted".
the "attribute.specified" is different in DOM level3 from DOM level2 spec.
1.same point
True if this attribute was explicitly given a value in the instance document, false otherwise. If the application changed the value of this attribute node (even if it ends up having the same value as the default value) then it is set to true.
2.Differences
"DOM-Level2" has more complicated judgement condition to decide whether the "attribute.specified" is true or not.
"DOM-Level3" says
The implementation may handle attributes with default values from other schemas similarly but applications should use Document.normalizeDocument() to guarantee this information is up-to-date.
search specified of type boolean, readonly in
https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-637646024
vs
https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-862529273

Related

AEM 6.0: Additional parameters when using data-sly-resource?

I am trying to implement something which I hope is relatively straight forward... I have one component (lets call it the wrapper component) which contains another component (lets call it the inner component) inside it via the data-sly-resource tag:
<div data-sly-resource="${ 'inner' # resourceType='/projectname/components/inner' }"></div>
I would like to pass in some additional parameters with this tag, specifically a parameter that can be picked up by sightly in the inner component template? I am trying to specify whether the inner templates outer html tag is unwrapped based on a parameter being passed in when the component is called via data-sly-resource.
After experimenting and perusing the sightly documentation, I can't find a way of achieving this.
Does anyone know if this is possible?
Many thanks,
Dave
You can use the Use-API to write and read request attributes if the alternatives proposed here don't work for you.
A quick example of two components where the outer component sets attributes that are then displayed by the inner component:
/apps/siteName/components/
outer/ [cq:Component]
outer.html
inner/ [cq:Component]
inner.html
utils/ [nt:folder]
setAttributes.js
getAttributes.js
/content/outer/ [sling:resourceType=siteName/components/outer]
inner [sling:resourceType=siteName/components/inner]
/apps/siteName/components/outer/outer.html:
<h1>Outer</h1>
<div data-sly-use="${'../utils/setAttributes.js' # foo = 1, bar = 2}"
data-sly-resource="inner"></div>
/apps/siteName/components/inner/inner.html:
<h1>Inner</h1>
<dl data-sly-use.attrs="${'../utils/getAttributes.js' # names = ['foo', 'bar']}"
data-sly-list="${attrs}">
<dt>${item}</dt> <dd>${attrs[item]}</dd>
</dl>
/apps/siteName/components/utils/setAttributes.js:
use(function () {
var i;
for (i in this) {
request.setAttribute(i, this[i]);
}
});
/apps/siteName/components/utils/getAttributes.js:
use(function () {
var o = {}, i, l, name;
for (i = 0, l = this.names.length; i < l; i += 1) {
name = this.names[i];
o[name] = request.getAttribute(name);
}
return o;
});
Resulting output when accessing /content/outer.html:
<h1>Outer</h1>
<div>
<h1>Inner</h1>
<dl>
<dt>bar</dt> <dd>2</dd>
<dt>foo</dt> <dd>1</dd>
</dl>
</div>
As commented by #AlasdairMcLeay, this proposed solution has an issue in case the inner component is included multiple times on the request: the subsequent instances of the component would still see the attributes set initially.
This could be solved by removing the attributes at the moment when they are accessed (in getAttributes.js). But this would then again be a problem in case the inner component is split into multiple Sightly (or JSP) files that all need access to these attributes, because the first file that accesses the request attributes would also remove them.
This could be further worked-around with a flag telling wether the attributes should be removed or not when accessing them... But it also shows why using request attributes is not a good pattern, as it basically consists in using global variables as a way to communicate among components. So consider this as a work-around if the other two solutions proposed here are not an option.
There is a newer feature that request-attributes can be set on data-sly-include and data-sly-resource :
<sly data-sly-include="${ 'something.html' # requestAttributes=amapofattributes}" />
Unfortunately it doesn't seem to be possible to construct a Map with HTL (=Sightly) expressions, and I don't see a way to read a request attribute from HTL, so you still need some Java/Js code for that.
unfortunately, no. there is no way to extend sightly functionality. you cannot add new data-sly attributes or modify existing ones. The best you can do is write your own helper using the USE API
If you just need to wrap or unwrap the html from your inner component in different situations, then you can just keep the html in the component unwrapped, and wrap it only when needed by using the syntax:
<div data-sly-resource="${ 'inner' # resourceType='/projectname/components/inner', decorationTagName='div', cssClassName='someClassName'}"></div>
If you need more complex logic, and you need to pass a value to your inner component template, you can use the selectors. The syntax for including the resource with selectors is:
<div data-sly-resource="${ 'inner' # resourceType='/projectname/components/inner', selectors='mySelectorName'}"></div>
The syntax to check the selectors in the inner component is:
${'mySelectorName' in request.requestPathInfo.selectorString}"
or
${'mySelectorName' == request.requestPathInfo.selectorString}"

Unable to set data attribute of true or false using Sightly data

I'm trying to set a data attribute with a value of true/false using Sightly but its always removing the attribute from the div. I can hardcode the true or false and it works but using Sightly it just removes it.
code example
<div data-video-controls="${videoPlayer.videoControls}"></div>
It just returns
<div></div>
I've also tried the context but still to no avail.
It seems there is a bug in the way how Sightly handles boolean-named string ${'false'}. Consider following examples:
// Example 1
In: <div true1="${true}" false1="${false}">
Out: <div true1>
// Example 2
In: <div true2="true" false2="false">
Out: <div true2="true" false2="false">
// Example 3 - bug
In: <div true3="${'true'}" false3="${'false'}">
Out: <div true3="true">
Example 1 shows that if you assign ${true} or ${false} boolean to the attribute, the attribute will be shown (valueless) or hidden. Example 2 isn't surprising - it's a normal HTML.
Example 3 presents the bug. We use ${'true'} and ${'false'} string literals and they should be presented as any other strings (eg. ${'hello world'}). ${'true'} works predictably, but ${'false'} is evaluated (like ${false}) and it hides the attribute. I wasn't able to produce a Sightly attribute that would produce something like: attr="false".
If it worked correctly, you could transform the Boolean into a String using the ternary operator:
<div data-video-controls="${videoPlayer.videoControls ? 'true' : 'false'}"></div>
Because of the bug it produces correct results only for the videoControls == true. Otherwise it hides the attribute.
NielsInc mentioned the following in the comments:
It's also possible to set the context to 'text', or another context which might suit your use case better. This way the boolean value will be rendered as text and the false value will be displayed: ${videoPlayer.videoControls # context='text'}
Using ${videoPlayer.videoControls # context='text'} worked for me.

KnockoutJS and Property-Dependent Create/Edit-View (Master/Detail Scenario)

I am having trouble creating a property-dependent create/edit-view in KnockoutJS.
Here's the thing: everything I create are "People" of sorts - it could be a Healthcare Professional, Plumber, Mechanic or Engineer. Depending on what kind/type of person it is, I need to enter different data.
Here an example:
Healthcare Professional: Name, Telephone, Hospital, etc.
Plumber: Name, Telephone, Crafts, etc.
Engineer: Name, Telephone, Specialities, etc.
What I can do is create properties on my ViewModels such as "showCity", "showHospital" and so on to hide individual form-fields.
However, for the sake of separation, I would like to use entirely different forms: again, I could set the respective form to only show if the condition is met.
However, I would like KnockoutJS to only render the respective form that should be used (the Person's type is always determined when it is first created - it cannot be changed).
What I don't end-up doing is have one form that is shown and ten that are there (and data-bound) but hidden.
I tried using the "if" binding like so: <div data-bind="with: $root.selectedPerson"><form data-bind="if: $data.type='mathematician'"></form></div>, but to no avail.
Would anybody know what the best-practice is in this case?
Your if binding is setting the $data.type value, not comparing it. Try:
<div data-bind="with: $root.selectedPerson"><form data-bind="if: $data.type() === 'mathematician'"></form></div>
Although this is fine, I always try to avoid code in my data-binding markup. I would try and create a computed that would return the resulting true/false of the comparison, but in your situation, you would need one for each person type, and that would get tricky. For that, I would turn to templates. You could do:
<div data-bind="template: { name: $root.selectedPerson().type, data: $root.selectedPerson }"></div>
<script type="text/html" id="mathematician">...</script>
<script type="text/html" id="plumber">...</script>
*Note: As of KO version 2.3.0, the name property of the template binding can accept observables. If you're using a previous version, be sure to call the observable in the binding: name: $root.selectedPerson().type()

innerHTML of an element without id or name attributes

Why is the following code NOT working without id or name attribute specified for the anchor element?
<html>
<body>
First link
<p>innerHTML of the first anchor:
<script>document.write(document.anchors[0].innerHTML);</script>
</p>
</body>
</html>
But if I add an id (or name) attribute, like that:
<a id="first" href="#">First link</a>
It starts to work.
Why is id or name attribute so important? I don't refer to it in my javascript code. I don't use "getElementById" or anything, but it still wants an id to be specified.
P.S. I tested only in IE7 (not the best browser, but I don't have access to anything better at the moment, and it can't stop me from learning :)
UPDATE:
Thanks to Raynos who gave me an idea of HTMLCollection in his answer, I've gotten a deeper understanding of what's going on here, by searching the web.
When we use document.anchors collection, we're actually referring to a collection of a elements with the name attribute that makes an a element behave as an anchor, and not (only) as a link.
We don't have to specify the name attribute if we want to refer to a elements as links. In this case we just need to use a different instance of HTMLCollection object which is document.links.
So the original code will work without name attribute if we modify it to:
document.write(document.links[0].innerHTML);
What a nice feeling of enlightenment! :)
WHATWG says:
The anchors attribute must return an HTMLCollection rooted at the Document node, whose filter matches only a elements with name attributes.
the document.anchors collection needs <a> elements with a name attribute.
IE is known to have bugs where it treats id's and name's as the "same" thing. So that would probably explain why it works for <a> elements with an id attribute.
As an aside, document.write and .innerHTML are evil.
Why don't you use this:
document.getElementsByTagName('a')[0].innerHTML

jQuery: Select all 'select' elements with certain val()

Does anyone know of an easy way, using jQuery, to select all <select> elements whose val() attribute yields a certain value?
I'm trying to do some validation logic and would like to just select all those elements with a single selector, then apply a warning class to each of their parents. This I know how to do once I select all the elements, but I didn't see a selector that handles this case.
Am I going to have to select all of the <select> elements into a selector, then iterate through them and check each of their values? I was hoping there would be a simpler way.
Thanks.
Why doesn't select[value=x] work? Well firstly because <select> doesn't actually have a value attribute. There is not a single value of a select box: there may be no selected options (there shouldn't normally be, but there can be in at least IE), and, in a <select multiple>, there can be any number of selected options.
Even input[value=x] doesn't work, even though <input> does have a value attribute. Well, it does work, it just doesn't do what you think. It fetches the value of the value="..." attribute in the HTML, not the current value you have entered into the form. The value="..." attribute actually corresponds to the defaultValue property and not value.
Similarly, option[value=x][selected] doesn't work because it is checking the <option selected> attribute from the HTML source (selected attribute -> defaultSelected property) and not the current selectedness of the option (selected property not attribute) - which might have changed since the page was loaded.
Except in IE, which gets the value, selected etc form attributes wrong.
Except (again): Tesserex's example may seem to work, and the reason for that is that that it's using a non-standard jQuery-specific selector, :has. This causes the native querySelectorAll methods of modern browsers to fail, and consequently jQuery falls back to its own (native JavaScript, slow) selector engine instead. This selector engine has a bug where it confuses properties for attributes, allowing [value=x] to do what you expected, and not fail like it should! (Update: this is probably no longer the case in newer jQuery versions.)
Summary: form field state checking and selectors don't mix. Apart from these issues, you also have to worry about escaping issues - for example, what if the value you want to test against contains quotes or square brackets?
So instead, yes, you should check it manually. For example using a filter:
$('select').filter(function() {
return $(this).val()==='the target value';
}).parent().addClass('warning');
(There is a value property in HTML5 and supported by modern browsers, that when you read it gives you the value of the first selected <option>. jQuery's val() is safe to use here because it provides the same method of getting the first selected option even on browsers that don't support this.)
The existing answers don't work on select tags, but I found something that does. Ask for a select that has a selected option.
$("select:has(option[value=blah]:selected)")
You can use :
$("select[value=X]");
where X is the value against which you want to check the select's value.
Attribute selectors Is what you're looking for I believe.
Something like $+('element[attribute="value"]')
See also:
*= anywhere
^= starts with
$= ends with
~= contains word
etc.
You can create a change event that puts the value in a custom attribute on the select element whenever the value changes. You can then use a simple selector to find all of the select elements that have that value. For example:
$("select").on("change", function (e) {
var $select = $(e.currentTarget);
$select.attr("select-value", $select.val());
});
And then you can do this:
var $matches = $("select[select-value='" + searchVal + "']");
$matches will have all of your matching selects.
This is a lot easier than having to iterate through elements. Remember to set select-value to the initial value when rendering the page so you don't need to trigger a change event for each select so the select-value is set.