Skip to content

Instantly share code, notes, and snippets.

@datchley
Last active August 29, 2015 14:07
Show Gist options
  • Select an option

  • Save datchley/e1b7683cc256f36ee405 to your computer and use it in GitHub Desktop.

Select an option

Save datchley/e1b7683cc256f36ee405 to your computer and use it in GitHub Desktop.
Class-like properties on elements in jQuery
!function (factory) {
if (typeof exports == 'object') {
factory(require('jquery'));
} else if (typeof define == 'function' && define.amd) {
define(['jquery'], factory);
} else {
factory(jQuery);
}
}(function($) {
'use strict';
// Class-like Properties
// Implementation of class-like properties for jQuery objects, allowing
// a given property to be manipulated like the class attribute using
// methods similar to jQuery's class related methods.
$.fn.extend({
/**
* hasCProp - check if the class-like property has a value(s).
* @param cprop {string} - name of class-like property
* @param [vals] {string} - space separated values to check for
* @returns {boolean} - true if it has all the values, false otherwise
*/
hasCProp: function(cprop, vals) {
var _check = vals.split(/\s+/),
_old = this.attr(cprop);
for (var i=0, l=_check.length; i<l; i++) {
var value = _check[i];
if (_old.indexOf(value) === -1) {
return false;
}
}
return true;
},
/**
* addCProp - add values to a class-like property of an element,
* retaining existing property values.
* @param cprop {string} - name of class-like property
* @param [vals] {string} - space separated values to append to property
* @returns {jQuery} - original jQuery object context
*/
addCProp: function(cprop, vals) {
if (!cprop) {
throw Error('missing argument to $.addCProp');
}
var _old,
_add = vals.split(/\s+/);
this.each(function() {
_old = $(this).attr(cprop).split(/\s+/);
for (var i=0, l = _add.length; i<l; i++) {
var value = _add[i];
if (_old.indexOf(value) === -1) {
_old.push(value);
}
}
$(this).attr(cprop, _old.join(' '));
});
return this;
},
/**
* removeCProp - remove values from a class-like property of an element,
* while retaining existing property values not in the list to be removed.
* @param cprop {string} - name of class-like property
* @param [vals] {string} - space separated values to remove from property
* @returns {jQuery} - original jQuery object context
*/
removeCProp: function(cprop, vals) {
if (!cprop) {
throw Error('missing argument to $.removeCProp');
}
var _old,
_remove = vals.split(/\s+/);
this.each(function() {
_old = $(this).attr(cprop).split(/\s+/);
for (var i=0, l = _remove.length; i<l; i++) {
var value = _remove[i],
idx = _old.indexOf(value);
if (idx > -1) {
_old.splice(idx, 1);
}
}
$(this).attr(cprop, _old.join(' '));
});
return this;
},
/**
* toggleCProp - toggle an individual cprop value or
* the full cprop attribute value.
* @param cprop {string} - name of the class-like property
* @param [values] {string} - the values to toggle, space separated
* @param [state] {boolean} - flag to determine whether values should be added or removed
* @return {jQuery} - original jQuery object context
*/
toggleCProp: function(cprop, values, state) {
var type = typeof values,
reNoWS = /\S+/g;
if (typeof state === 'boolean' && type === 'string') {
return state ? this.addCProp(cprop, values) : this.removeCProp(cprop, values);
}
return this.each(function() {
var $_this = $(this);
if (type === "string") {
// Toggle individual cprop values
var i = 0,
cprops = values.match(reNoWS) || [],
cpropval;
while ((cpropval = cprops[i++])) {
// Check each cprop given, space separated list
if ( $_this.hasCProp(cprop, cpropval)) {
$_this.removeCProp(cprop, cpropval);
} else {
$_this.addCProp(cprop, cpropval);
}
}
}
else if (values === undefined || type === 'boolean') {
// Toggle entire cprop value
var cpropfull = $(this).attr(cprop);
if (cpropfull) {
// store the full value
$_this.data('__'+cprop+'__', cpropfull);
}
// toggle full value if it has a cprop, based on value
$_this.attr(cprop, (cpropfull || values === false)
? ""
: $_this.data('__'+cprop+'__') || "");
}
});
}
});
});
// Test of cprop methods - JSBin: http://jsbin.com/qaveze/2/edit
var $el = $('#elm');
// setup a cprop
$el.attr('test-prop', 'a b c');
// check for a cprop value(s)
console.log("[0] test-prop has (a)? " + $el.hasCProp('test-prop','a'));
console.log("[1] test-prop has (x)? " + $el.hasCProp('test-prop','x'));
// [0] test-prop has (a)? true
// [1] test-prop has (x)? false
// add (append) cprop values
$el.addCProp('test-prop', 'd e f');
console.log("[2] test-prop = " + $el.attr('test-prop'));
// [2] test-prop = a b c d e f
// remove cprop values, chained add
$el.removeCProp('test-prop', 'e').addCProp('test-prop', 'g h');
console.log("[3] test-prop = " + $el.attr('test-prop'));
// [3] test-prop = a b c d f g h
// Test toggling of class-like properities
$('.toggle-single').click(function() {
$(this).toggleCProp('test-toggle-prop', "A")
.html("prop: " + $(this).attr('test-toggle-prop'));
});
$('.toggle-full').click(function() {
$(this).toggleCProp('test-toggle-prop')
.html("prop: " + $(this).attr('test-toggle-prop'));
});
<!DOCTYPE html>
<html>
<head>
<script src="//code.jquery.com/jquery-1.11.1.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<!-- class-like properties elements -->
<div id="elm" class="a b c" test-prop="a b c"></div>
<div class="a b c x" test-prop="a b c"></div>
<hr/>
<p>Toggle a single cprop value:</p>
<div class="toggle-single block off" test-toggle-prop="A B C">
prop: A B C
</div>
<p>Toggle entire prop</p>
<div class="toggle-full block off" test-toggle-prop="A B C">
prop: A B C
</div>
<script src="cprop.test.js"></script>
</body>
</html>
@datchley
Copy link
Copy Markdown
Author

datchley commented Oct 2, 2014

Really, the $.cprop method is just a wrapper around $.attr; and really could be written as:

cprop: function(){ return this.attr.apply(this, [].slice.call(arguments)); }

and really, probably isn't needed, as we could just replace calls to cprop() with attr() in the remaining code.

update made this change to the gist

@datchley
Copy link
Copy Markdown
Author

datchley commented Oct 2, 2014

Ensured the addCProp() and removeCProp() methods worked like the jQuery class methods and work on collections of objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment