Attributes and Properties page
Learn about the attributes and properties on an element.
Overview
We will learn about:
- The difference between attributes and properties
- How to set and read styles on an element
Slides
Setup
Run the following example in CodePen:
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<link rel="stylesheet" href="//bitovi.github.io/academy/static/scripts/jquery-test.css">
<script type="module">
(function() {
$ = function(selector) {
if ( !(this instanceof $) ) {
return new $(selector);
}
var elements;
if (typeof selector === "string") {
elements = document.querySelectorAll(selector);
} else if ($.isArrayLike(selector)) {
elements = selector;
}
[].push.apply(this, elements);
};
$.extend = function(target, object) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
target[prop] = object[prop];
}
}
return target;
};
// Static methods
$.extend($, {
isArray: function(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
},
isArrayLike: function(obj) {
return obj &&
typeof obj === "object" &&
( obj.length === 0 ||
typeof obj.length === "number" &&
obj.length > 0 &&
obj.length - 1 in obj );
},
each: function(collection, cb) {
if ($.isArrayLike(collection)) {
for (var i = 0; i < collection.length; i++) {
if (cb.call(this, i, collection[i]) === false) {
break;
}
}
} else {
for (var prop in collection) {
if (collection.hasOwnProperty(prop)) {
if (cb.call(this, prop, collection[prop]) === false) {
break;
}
}
}
}
return collection;
},
makeArray: function(arr) {
if ($.isArray(arr)) {
return arr;
}
var array = [];
$.each(arr, function(i, item) {
array[i] = item;
});
return array;
},
proxy: function(fn, context) {
return function() {
return fn.apply(context, arguments);
};
}
});
function makeTraverser(traverser) {
return function() {
var elements = [], args = arguments;
$.each(this, function(i, element) {
var els = traverser.apply(element, args);
if ($.isArrayLike(els)) {
elements.push.apply(elements, els);
} else if (els) {
elements.push(els);
}
});
return $(elements);
};
}
$.extend($.prototype, {
html: function(newHtml) {
if(arguments.length) {
return $.each(this, function(i, element) {
element.innerHTML = newHtml;
});
} else {
return this[0].innerHTML;
}
},
val: function(newVal) {
if(arguments.length) {
return $.each(this, function(i, element) {
element.value = newVal;
});
} else {
return this[0].value;
}
},
text: function(newText) {
if (arguments.length) {
return $.each(this, function(i, element) {
element.textContent = newText;
});
} else {
return this[0].textContent;
}
},
find: makeTraverser(function(selector) {
return this.querySelectorAll(selector);
}),
parent: makeTraverser(function() {
return this.parentNode;
}),
next: makeTraverser(function() {
return this.nextElementSibling;
}),
prev: makeTraverser(function() {
return this.previousElementSibling;
}),
children: makeTraverser(function() {
return this.children;
}),
attr: function(attrName, value) { },
css: function(cssPropName, value) { },
addClass: function(className) { },
removeClass: function(className) { }
});
})();
</script>
Each exercise builds on the previous exercise. There is a completed solution at the end of this page.
Exercise: collection.attr( attrName [,value] )
The problem
collection.attr either:
- Gets the value of an attribute for the first element in the set of matched elements, or
- sets one or more attributes for every matched element.
<a id="link-less">Bitovi</a>
<script type="module">
import "https://unpkg.com/jquery@3/dist/jquery.js";
$("a").attr("href", "https://bitovi.com");
</script>
Click to see test code
QUnit.test("$.fn.attr", function () {
equal($("#qunit-fixture").attr("id"), "qunit-fixture", "can read id");
$("#qunit-fixture").html("<span></span><span></span>");
$("#qunit-fixture span").attr("foo", "bar");
equal(
$("#qunit-fixture span")[0].getAttribute("foo"),
"bar",
"attribute set successfully"
);
equal(
$("#qunit-fixture span")[1].getAttribute("foo"),
"bar",
"attribute set successfully"
);
$("#qunit-fixture span")[0].setAttribute("foo", "BAR");
equal(
$("#qunit-fixture span").attr("foo"),
"BAR",
"read the first item in the collection’s attr"
);
});
What you need to know
- getAttribute reads an attribute value
- setAttribute sets an attribute value
The solution
Click to see the solution
attr: function(attrName, value) {
if (arguments.length == 2) {
return $.each(this, function(i, element) {
element.setAttribute(attrName, value);
});
} else {
return this[0] && this[0].getAttribute(attrName);
}
},
Exercise: collection.css( propertyName [,value] )
The problem
collection.css either:
- Gets the value of a computed style property for the first element in the set of matched elements,
- or sets one or more CSS properties for every matched element.
<div>Foo Bar</div>
<script type="module">
import "https://unpkg.com/jquery@3/dist/jquery.js";
$("div").css("backgroundColor", "green");
</script>
Click to see test code
QUnit.test("$.fn.css", function () {
$("#qunit-fixture").html("<span>Content</span><span>Second</span>");
equal($("#qunit-fixture span").css("padding-left"), "20px");
$("#qunit-fixture span").css("paddingLeft", "40px");
equal(
$("#qunit-fixture span").css("padding-left"),
"40px",
"first span set to 40px"
);
equal(
$("#qunit-fixture span:nth-child(2)").css("padding-left"),
"40px",
"second span set to 40px"
);
});
What you need to know
The style property is used to set and get the inline style of an element.
<div id="theDiv">theDiv</div> <script type="module"> theDiv.style.color = "red"; console.log(theDiv.outerHTML); // Logs "<div id="theDiv" style="color: red;">theDiv</div>" </script>
The window.getComputedStyle returns an object containing the values of all CSS properties of an element.
<p id="theP">Hello</p> <style> p { color: green; } </style> <script type="module"> let computedStyles = window.getComputedStyle(theP); console.log(computedStyles.getPropertyValue("color")); // logs "rgb(0, 128, 0)" </script>
The solution
Click to see the solution
css: function(cssPropName, value) {
if (arguments.length == 2) {
return $.each(this, function(i, element) {
element.style[cssPropName] = value;
});
} else {
return this[0] &&
window.getComputedStyle(this[0])
.getPropertyValue(cssPropName);
}
},
Bonus Exercise: collection.addClass(className)
and collection.removeClass(className)
The problem
collection.addClass adds a class to each element’s className
.
collection.removeClass removes a class to each element’s className
.
The following changes the <div>
from green to red after one second.
<style>
.red {
background-color: red;
}
.green {
background-color: green;
}
</style>
<div class="red" id="hi">Hello</div>
<script type="module">
import "https://unpkg.com/jquery@3/dist/jquery.js";
setTimeout(function () {
$("#hi").addClass("green").removeClass("red");
}, 1000);
</script>
Click to see test code
QUnit.test("$.fn.addClass and $.fn.removeClass", function () {
var count = function (reg, str) {
var c = 0;
str.replace(reg, function () {
c++;
});
return c;
};
var $divs = $("#qunit-fixture")
.html('<div class="foo"></div><div class="foob"></div>')
.children();
$divs.addClass("foo");
equal(1, count(/foo/, $divs[0].className), "only one foo");
equal(1, count(/foo/, $divs[1].className), "only one foo");
$divs.addClass("foob");
equal(1, count(/foob/, $divs[0].className), "only one foo");
equal(1, count(/foob/, $divs[1].className), "only one foo");
$divs.removeClass("foob");
equal(0, count(/foob/, $divs[0].className), "only one foo");
equal(0, count(/foob/, $divs[1].className), "only one foo");
$divs.removeClass("foo");
equal(0, count(/foo/, $divs[0].className), "only one foo");
equal(0, count(/foo/, $divs[1].className), "only one foo");
});
What you need to know
An element’s classList lets you add and remove class names on it.
<style> .red { background-color: red; } .green { background-color: green; } </style> <div class="red" id="hi">Hello</div> <script type="module"> setTimeout(function () { hi.classList.add("green"); hi.classList.remove("red"); }, 1000); </script>
The solution
Click to see the solution
addClass: function(className) {
return $.each(this, function(i, element) {
element.classList.add(className);
});
},
removeClass: function(className) {
return $.each(this, function(i, element) {
element.classList.remove(className);
});
}
Complete Solution
Click to see completed solution
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<link rel="stylesheet" href="//code.jquery.com/qunit/qunit-1.12.0.css">
<script src="//code.jquery.com/qunit/qunit-1.12.0.js"></script>
<script src="//bitovi.github.io/academy/static/scripts/jquery-test.js"></script>
<link rel="stylesheet" href="//bitovi.github.io/academy/static/scripts/jquery-test.css">
<script type="module">
(function() {
$ = function(selector) {
if ( !(this instanceof $) ) {
return new $(selector);
}
var elements;
if (typeof selector === "string") {
elements = document.querySelectorAll(selector);
} else if ($.isArrayLike(selector)) {
elements = selector;
}
[].push.apply(this, elements);
};
$.extend = function(target, object) {
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
target[prop] = object[prop];
}
}
return target;
};
// Static methods
$.extend($, {
isArray: function(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
},
isArrayLike: function(obj) {
return obj &&
typeof obj === "object" &&
( obj.length === 0 ||
typeof obj.length === "number" &&
obj.length > 0 &&
obj.length - 1 in obj );
},
each: function(collection, cb) {
if ($.isArrayLike(collection)) {
for (var i = 0; i < collection.length; i++) {
if (cb.call(this, i, collection[i]) === false) {
break;
}
}
} else {
for (var prop in collection) {
if (collection.hasOwnProperty(prop)) {
if (cb.call(this, prop, collection[prop]) === false) {
break;
}
}
}
}
return collection;
},
makeArray: function(arr) {
if ($.isArray(arr)) {
return arr;
}
var array = [];
$.each(arr, function(i, item) {
array[i] = item;
});
return array;
},
proxy: function(fn, context) {
return function() {
return fn.apply(context, arguments);
};
}
});
function makeTraverser(traverser) {
return function() {
var elements = [], args = arguments;
$.each(this, function(i, element) {
var els = traverser.apply(element, args);
if ($.isArrayLike(els)) {
elements.push.apply(elements, els);
} else if (els) {
elements.push(els);
}
});
return $(elements);
};
}
$.extend($.prototype, {
html: function(newHtml) {
if(arguments.length) {
return $.each(this, function(i, element) {
element.innerHTML = newHtml;
});
} else {
return this[0].innerHTML;
}
},
val: function(newVal) {
if(arguments.length) {
return $.each(this, function(i, element) {
element.value = newVal;
});
} else {
return this[0].value;
}
},
text: function(newText) {
if (arguments.length) {
return $.each(this, function(i, element) {
element.textContent = newText;
});
} else {
return this[0].textContent;
}
},
find: makeTraverser(function(selector) {
return this.querySelectorAll(selector);
}),
parent: makeTraverser(function() {
return this.parentNode;
}),
next: makeTraverser(function() {
return this.nextElementSibling;
}),
prev: makeTraverser(function() {
return this.previousElementSibling;
}),
children: makeTraverser(function() {
return this.children;
}),
attr: function(attrName, value) {
if (arguments.length == 2) {
return $.each(this, function(i, element) {
element.setAttribute(attrName, value);
});
} else {
return this[0] && this[0].getAttribute(attrName);
}
},
css: function(cssPropName, value) {
if (arguments.length == 2) {
return $.each(this, function(i, element) {
element.style[cssPropName] = value;
});
} else {
return this[0] &&
window.getComputedStyle(this[0])
.getPropertyValue(cssPropName);
}
},
addClass: function(className) {
return $.each(this, function(i, element) {
element.classList.add(className);
});
},
removeClass: function(className) {
return $.each(this, function(i, element) {
element.classList.remove(className);
});
}
});
})();
</script>