jQuery and HTML5 valueless (data) attributes - javascript

I have data attributes I'm using for options on a table, for example:
<table data-do-something-neat>
...
</table>
I'm using HAML to define them:
%table{ data: { do_something_neat: true } }
...
In HAML it's an HTML5 formatted page, so it's behavior is to drop the value if it's a boolean and true. If it were set to false, it would be gone, like so:
<table>
...
</table>
All this seems fine, but accessing whether that flag is true or false in jQuery is a bit of a pain, and I can't find any definitive reference on how to do it.
For the first table, the following is true:
table.is('[data-do-something-neat]') # true
table.attr('data-do-something-neat') # '' (empty string)
table.data('do-something-neat') # '' (empty string)
And for the second table:
table.is('[data-do-something-neat]') # false
table.attr('data-do-something-neat') # undefined
table.data('do-something-neat') # undefined
So attr and data return something falsy for a true HTML5 data attribute. Is this expected? Is there another data method that could be used? Or am I stuck using is in this way? It's unfortunate to have to use a special method for boolean attributes, instead of just using data for all data attributes.

It kind of makes sense that you're stuck using .is(). Some data attributes should be treated as Booleans, and some should be treated as strings.
Imagine if attr() returned true for an empty string; it would be difficult to test for, and in order to have it properly appear as 'null', your server code would need to write:
<table
<?php if $accountId != null {?>
data-accountId="<?php echo $accountId; ?>"
<?php } ?> >
(The emphasis being on the outside null-checking condition). But, since it returns an empty string, you can simply use javascript and use any standard "is empty string" method you prefer, or just check "if length == 0" if you know the attribute should always be printed from the server.

The difference is that if the attribute is not there, $.attr and $.data return undefined, if it's there without a value, it returns an empty string, which is the expected behavior, as far as I know.
What is the problem with checking?
if (typeof table.attr('data-do-something-neat') !== 'undefined') {
// attribute exists, but it could be the empty string
}
If you want a more straight forward way to test it, you can use Element.hasAttribute
if (table[0].hasAttribute('data-do-something-neat')) {
// attribute exists, but it could be the empty string
}

I would just use a selector and check for the existance of a correctly selected element:
$('table[data-do-something-neat]').length !== 0
or
$('#A').attr('myattr') !== undefined // If attribute exists
That's what's noted in this SO question: Select elements by attribute
Or if you can go without jQuery there are native DOM methods that will suffice:
Element.hasAttribute('data-do-something-neat');

Related

Javascript Bug: set audio object controls attribute True = = False

I'm rather new to this and don't know exactly how to report a bug, but I first want to confirm it's a bug and then go on from there. But here's what I'm finding:
1) When creating an audio object controls attribute, the controls attribute will respond to a string as if it's a boolean.
For Instance:
<button onclick="example()">Try this</button>
<script>
function example() {
var aud = document.createElement("AUDIO");
aud.setAttribute("src","example.mp3");
aud.setAttribute("controls", "controls");
}
Okay, we've put controls in there because it makes controls equal controls:
Thing is, you can put any old string in there and it works just fine -- apple, banana, pear, anything.
2) Isn't the value suppose to be a boolean? Well when you try a boolean, false for example, you still get true. (False == True) It works just as if you typed in true.
...and if you put anything else other than true or false (just type anything other than an integer, string, or true or false value), you get false (or it just doesn't work).
Everything equals true and a non-string, non-integer equals false (or just doesn't work).
Finally, you can even try setting the controls attribute on an accessed audio element:
var aud = document.getElementById("idhere");
function accessAudioElement() {
aud.controls = false;
}
At least here the true and false actually work as true and false, but once again, any string or integer will also get you true and any non-string/non-integer will break the code.
Can somebody help me out here because I don't think this is suppose to work this way... and if does, what's the point of using a boolean value when most anything else will work?
Of course I'm still learning, so maybe this is not a bug, maybe for some reason this is suppose to work this way, but if that's the case would someone please share the logic behind this with me.
I'm just not understanding.
Thanks Magic
This is an extended answer of what #nnnnnn suggested in the comments.
aud.controls = false; doesn't set the attribute, it sets the property.
You need to use setAttribute() method to add the specified attribute to an element.
aud.setAttribute("controls", "controls");
And use removeAttribute() method removes the specified attribute from an element.
aud.removeAttribute("controls");
For more reading on these methods, have a look at the hyperlinks attached.
Element.setAttribute()
Element.removeAttribute()
When to use setAttribute vs .attribute= in JavaScript?
HTML - attributes vs properties
You might want to read/search more about Javascript Truthy $ Falsey. It is very important.
https://j11y.io/javascript/truthy-falsey/

Check if params was passed or not

I have this Javascript function which i use to append elements do my form. When the form is submitted, all elements are passed in as params.
Here's the problem. The elements that my function appends are part of a Hash. This basically works as the rails "nested-attributes".
All seems to be working fine, except when one of the "Hashs" is empty. When i delete all elements from the collection, the hash params is not passed to the controller.
Example (Imagine i appended Honda, Toyota, Hyundai using my JS function)
Cars
[Honda]
[Toyota]
[Hyundai]
This is how the hash is set up in the JS function (every time i click the "add"):
CarsHash.name= "cars_hash"+"["+"tmp_cars"+"]"+"["+i+"]"+"car_name"
If i were to submit the form, the values would be passed as Hash to the controller like this:
cars_hash=>{tmp_cars=>"{"1"=>{car_brand=> "honda"},"2"=>{car_brand=> "toyoda"},"3"=>{car_brand=> "hyundai"}"}
However, if i decide to delete all those values from the hash (using the delete button which also has functionality stated on the JS function) and submit the form, the Hash is not even present as a param. Then, when i get to my controller and i try to populate the variable i use for iterating/inserting into my DB:
Controller
cars_hash = params[:cars_hash][tmp_cars]
It gives me this error:
undefined method `[]' for nil:NilClass
I understand, the param is not even present so its essentially nil. I tried with all this possibly options:
if not params[:cars]["tmp_cars"].blank?
if not params[:cars]["tmp_cars"].empty?
if params[:cars]["tmp_cars"].present?
if params[:cars]["tmp_cars"] != nil
But no luck. Can anyone suggest me a way to get this to work? Again, the param will only be not nil IF the hash as a value. For it to have a value, an element must be appended to the document through the JS function.
Try doing:
cars_hash = params[:cars_hash] && params[:cars_hash][tmp_cars]
I would strongly recommend installling andand gem which is perfect for situations like this. With this gem you can wirte the code above like:
cars_hash = params[:cars_hash].andand[tmp_cars]
UPDATE:
Your code was not working because params[:cars_hash] returned nil, on which you were trying to call [] method, and such a method is not defined for nil object. Hence you need to check whether params[:cars_hash] is nil or not before you call anything on it.
&& operator have this nice property that it is not even executing the right argument whan the left argument is falsy - there is no point of doing this. Since every expression in ruby returns value of the last executed command, && returns whatever left expression returns if it is falsy (false or nil) and otherwise it runs the expression on its right side and returns its value. This is pretty common to use this && in this context in Ruby.

hasAttribute("id") replacement before IE8

I have used
element.hasAttribute('id')
in my code to test whether the element has an attribute id. But hasAttribute API is only compatible with browsers after IE8.Is there a similar API or technique which I can use to check the availability of an attribute for an element in my case "id".
In the absence of the hasAttribute method, you need to use getAttribute. This should return null if there is no attribute set, and an empty string otherwise. In practice, some browsers return an empty string, so there's no way in these browsers of telling whether it is an empty attribute or no attribute at all.
if ((element.getAttribute('id') === null) || (element.getAttribute('id') === '')) {
Just check element.id - it'll be an empty string if it's not set.
There's no need to use element.hasAttribute for those attributes that are mirrored by JS object properties.

Do html5 data attributes need a value? [duplicate]

This question already has answers here:
Are empty HTML data attributes valid?
(4 answers)
Closed 7 years ago.
I am wondering if html data attributes actually need a value to be applied?
I wonder this because often all we want to know is if the attribute is actually set to act as a flag. (sure we could use a class for this; but realistically unless you are going to style these items differently then the flags are more data than a semantic item).
A perfect example of this is if we want a link to scroll to it's target instead of jumping our jQuery code might look like:
$(document).on('click', '[data-scroll-link'], function(){/**do scroll**/});
I know in google chrome it is sufficient for the anchor to appear as
<a href="#bottom" data-scroll-link>Scroll to bottom</a>
But will that work everywhere? and is it even valid HTML5 (I believe it is due to the autofocus, autoplay etc attributes). or do we need:
Scroll to bottom
No. But...
As is common with all attributes, in the application/xhtml+xml serialisation, XML rules apply and the attribute must have an explicit name and (quoted) value.
So this question is really about the text/html serialisation, and therefore the relevant part of the HTML5 spec is Section 8 The HTML syntax
In particular, under attributes, it says:
Attributes can be specified in four different ways:
where the first of these is:
Empty attribute syntax
Just the attribute name. The value is implicitly the empty string.
It's necessary to understand though that the value is of string type, not of boolean type.
For example, with <input id="cb" type="checkbox" checked>, the "checked" attribute is reflected by a property that is either true or false. So
if (document.getElementById("cb").checked)
will evaluate to true for the above markup.
In contrast, with <input id="cb" type="checkbox" data-checked>, the "data-checked" attribute is reflected via the dataset object as a string. The value of this property is the empty string, which in JavaScript is falsey. So,
if (document.getElementById("cb").dataset.checked)
will evaluate to false for the above markup.
To do the equivalent test, compare the value for "not undefined". I.e.
if (document.getElementById("cb").dataset.checked !== undefined)
will evaluate to true for the above markup.
See http://jsfiddle.net/GAxvW/
Simple Boolean Test For Element Attributes
To expand on Alohci's excellent answer, the following is a simple, flexible way to test for a true boolean attribute value supplied using one of three standard HTML conventions: <foo data-bar/>, <foo data-bar="true"/>, or <foo data-bar="data-bar"/>.
var a = elem['data-bar'];
var aTrue = ( a != null && a !== false && a !== 0 && a.charAt(0) != 'f' &&
a.charAt(0) != 'n' );
With the code above, the value is false if undefined or set to one of: f*, n*, 0 (case-insensitive), and true if defined and set to one of: (empty string), (attribute name), (anything else).
Empty strings are evaluated to true here because HTML attributes without values are '' which equal false in JS (and something like <foo disabled/> should equal <foo disabled="true"/>). You can use the above code for more general string testing by removing != null && a !== false.

jQuery 1.3 - Issue with Setting an Attribute Value

This is my first stackoverflow question, so try to be nice. ;-D
My issue is this, I am refactoring some existing javascript code and using jQuery to do it. In several places I've come across javascript code similar to the following:
// some js code working with the customAttribute value
javascriptElementObject.customAttribue = void(0);
The javascriptElementObject is now a jQuery object and I have been attempting to use the following code to do the same thing:
// some js code working with the customAttribute value
javascriptElementObject.attr("customAttribute", void(0));
However, this does not seem to be doing anything. The following code works however:
javascriptElementObject.get(0).customAttribute = void(0);
I'm aware of jQuery's removeAttr() function, but have not used it so far because I don't know if it's equivalent to setting the attribute value to void(0).
So I guess that really means I have 2 questions:
Why doesn't the first jQuery version work?
Are .get(0).customAttribue = void(0); and .removeAttr("customAttribute); equivalent?
Thanks.
jQuery likes to overload its methods so:
obj.attr( name ) //retrieves the attribute value
obj.attr( name, value ) //sets the attribute
obj.attr( name, void(0) ) == obj.attr( name, null ) == obj.attr( name ) //i.e retrieving the attribute
You might want to try the following if you want to set an empty attribute
obj.attr( name, '' )
This will also apply to other methods jQuery.html() for example
What are you trying to accomplish?
If the goal is to remove the value in the name/value pair, you might as well just remove the attribute entirely. I'm not aware of any intrinsic value in maintaining an attribute that has no value; in less standards-compliant browsers it may even cause a problem.
In general, the syntax of $(selector).attr(name, value) and $(selector).removeAttr(name) work very well (at least I've never seen it fail.)
If you're trying to use void(0) to keep A HREFs from firing you'd be better off using a "return false" as the click event on those A tags.
The only way to work with custom attributes via jQuery objects is:
obj.get(0).myCustomAttr = 'some value';
That is because jQuery's attr() method will not work with custom attributes (except while applied on a XML-document).
Note also that meouw's answer regarding jQuery overloading functions is not precisely correct, because jQuery checks for the parameters passed to it in such a manner that:
jQuery.funcname(param)
and
jQuery.funcname(param, null)
differ, becacuse null !== undefined. For example:
var params_test = function(a) {
if (a === undefined) {
return 'called with no parameters';
} else {
return 'called with one parameter: ' + a;
}
};
params_test(); // results in 'called with no parameters'
params_test(null); // results in 'called with one parameter: null'
Uhmm, try this:
javascriptElementObject.attr("customAttribute", void(0));
var _void = javascriptElementObject.attr("customAttribute");
alert(_void);

Categories

Resources