$ toggle

Test using only data-xxx attributes to modify (toggle) other targeted elements.

by Laurens Maneschijn

HTML

<h1>
	Test using only data-xxx attributes to modify other targeted elements.
</h1>

<hr>

<block>
	<h2>Simple event handlers</h2>
	<p>
		Simple event handlers using a single data attribute (optionally with value).<br>
	</p>
	<p>
		Format: <c>data-&lt;action&gt;-&lt;target&gt;="optionalselector"</c>.<br>
		Actions: toggle, show, hide, slideup, slidedown.<br>
		Targets: target, siblings, next, prev, nextall, prevall, nextfirst, prevfirst.
	</p>
	<p>Examples:</p>
	<ul>
		<li>
			<c>data-toggle-target="#someid"</c> &rArr; 
			<c>$('#someid').toggle();</c>
			(targeting absolute)
		</li>
		<li>
			<c>data-toggle-next</c> &rArr; 
			<c>$(this).next().toggle();</c>
			(targeting relative from clicked element)
		</li>
		<li>
			<c>data-show-nextfirst=".shownextfirst"</c> &rArr; 
			<c>$(this).nextAll('.shownextfirst').first().show();</c>
		</li>
	</ul>
</block>

<block>
	<button data-toggle-target="#targetid">toggle-target="#targetid"</button>
	<box id="targetid">#targetid</box>
</block>

<block>
	<button data-toggle-target=".targets">toggle-target=".targets"</button>
	<box class="targets">.targets 1</box>
	<box class="targets">.targets 2</box>
</block>

<block>
	<button data-toggle-next="box">toggle-next</button>
	<box>box</box>
	<button data-toggle-next="box">toggle-next="box"</button>
	<box>box</box>
	<button data-toggle-next="notbox">toggle-next="notbox"</button>
	<box>box</box>
</block>

<block>
	<button data-toggle-nextfirst=".target">toggle-nextfirst=".target"</button>
	<box>box 1</box>
	<box class="target">box.target 2</box>
	<box class="target">box.target 3</box>
</block>

<block>
	<button data-toggle-nextall>toggle-nextall</button>
	<box>box next 1</box>
	<box>box next 2</box>
</block>

<block>
	<button data-toggle-nextall=".bold">toggle-nextall=".bold"</button>
	<box>box next 1</box>
	<box class="bold">box next 2 bold</box>
	<box>box next 3</box>
	<box class="bold">box next 4 bold</box>
</block>

<block>
	(NB: <c>prev</c> and <c>prevall</c> similar to...

CSS

* {
	box-sizing: border-box;
}
h1,h2,h3,h4,h5,h6{
	margin: 2px 0 0 0;
	font-size: 1em;
}
p{
	margin: 4px 0;
}
ul,ol,li {
	margin: 0;
}
/*
ul, ol, ul > li, ol > li{
	padding:0 0 0 0;
	-webkit-padding-start: 10px;
}
*/

.hidden {display: none;}
box, .box, block, .block {
	display: block;
	margin:2px 0;
	padding:1px 2px;
	border: 1px solid #ddd;
	background: #eee;
}

box,.box {
	display: inline-block;
	border: 2px solid #88f;
  background: rgba(200, 200, 255, 0.5);
}
.stack > * {
	float:left;
	clear:left;
}
.stack::after, .clearfix:after { 
  content: "";
  visibility: hidden;
  display: block;
  height: 0;
  clear: both;
}
button {text-align: left;}
c,code {
	display: inline-block;
	padding: 1px;
	margin: 1px;
	border: 1px dashed #888;
	font-family: monospace;
	white-space: pre;
	background: #eee;
}
.bold {font-weight: bold;}
.italic {text-decoration: italic;}
.underline {text-decoration: underline;}

JavaScript

// TODO: use namespacing for events (e.g. click.mycustomnamespace_toggle)
// TODO: use namespacing for data attributes (e.g. data-mycustomnamespace-xxx...)

function setupEventhandlers(){
	setupSimpleEventhandlers();
	setupComplexEventhandlers();
}
setupEventhandlers();

function setupSimpleEventhandlers() {
	var simpleactions = ['toggle','show','hide','slideUp','slideDown'];
	var simpletargets = ['target','siblings','next','prev','nextAll','prevAll','nextfirst','prevfirst'];
	// create a batch of delegated event handlers for combined data attributes:
	// [data-<action>-<target>]
	// TODO: event namespace + .off()
	$(document.body).off('.simpleactiontarget');
	$.each(simpleactions, function(i1, action){
		$.each(simpletargets, function(i2, target){
			var data_action = action.toLowerCase();
			var data_target = target.toLowerCase();
			var jqfn = action;
			var jqtarget = target;
			var data = data_action + '-' + data_target;
			var selector = '[data-' + data + ']';
			var event_namespace = '';
			$(document.body).on('click.simpleactiontarget', selector, function(e){
				var d = $(e.currentTarget).data(data) || undefined;
				if(target === 'target'){
					$(d).toggle();
				} else if(target === 'nextfirst'){
					$(e.currentTarget).nextAll(d).first()[jqfn]();
				} else if(target === 'prevfirst'){
					$(e.currentTarget).prevAll(d).first()[jqfn]();
				} else {
					$(e.currentTarget)[jqtarget](d)[jqfn]();
				}
			});
		});
	});
}

/**
 * toggle (almost) anything by setting data attributes.
 * [data-toggle-relative] OR [data-toggle-selector] : start from current element, or targeted element
 * [data-toggle-closest] if given, from target above find closest parent (including self) matching given value as selector.
 * [data-toggle-find]    if given, from target above find closest parent (including self) matching given value as selector.
 */
function setupComplexEventhandlers(){
	$(document.body).on('click',...