$.fn.textFiltered(filter, nodecallback)

by Laurens Maneschijn

HTML

<table>
<thead>
  <tr>
    <th>col a</th>
    <th>col b</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td>td 1 1</td>
    <td>td 1 2 aaa <b>bold</b> bbb <a>anch</a> ccc </td>
  </tr>
  <tr>
    <td>td 2 1</td>
    <td>td 2 2 x <b> some  more   bold text    </b> y </td>
  </tr>
  <tr>
    <td>td 3 1</td>
    <td>td 3 2
      <select>
        <option>o1</option>
        <option selected>o2</option>
        <option>o3</option>
      </select>
    </td>
  </tr>
</tbody>
</table>

<button id="cleartests">Clear tests</button>
<button id="runtests">Run demo tests</button>
<button id="runtest">Run test:</button><br>
<textarea id="runtestcode">$('table').textFiltered('b', $.trim);</textarea>

<div id="tests" class="tests"></div>

CSS

table { border-collapse: collapse; }
table thead tr th,
table tbody tr td {
  border: 1px solid #ddd;
}

.test,
.test pre {
  display: block;
  margin: 2px;
  padding: 2px;
  border: 1px solid #ddd;
}
.test{ margin-bottom:10px; }
.code {
  border: 1px solid #ddd;
  background-color:#eee;
}
.result {
  border: 1px solid #ddd;
  background-color:#eee;
}

textarea { width:100%; }

JavaScript

////////////////////////////////////////
// attempt to make $.textFiltered() fetch only text from a subset of child elements
// skips unselected <OPTION> elements
// 2nd parameter takes either a function(elem){} that if returning false will skip that element and it's childnodes,
// or is skipped if it does not pass $(elem).is(filter) (i.e. it is used as a selector)
// getTextFiltered() is a copy from $.text, with added 'continue' skip filters
////////////////////////////////////////
(function($,jQuery){
	if(!$ || ! $.fn || ! $.fn.jquery){
		return alert('$.fn.jquery missing');
	}

	function getTextFiltered( elems , filter, nodecallback) {
		var ret = "", elem;

		for ( var i = 0; elems[i]; i++ ) {
			elem = elems[i];
			var is_comment_node = elem.nodeType === 8;
			var is_text_node    = elem.nodeType === 3;
			var is_cdata_node   = elem.nodeType === 4;

			if ( is_comment_node ) {
				continue;
			}else if ( is_text_node || is_cdata_node ) {
				var elemp = elem.parentNode;
				if(filter !== undefined && filter !== null && filter !== false && filter !== true){
					if ( jQuery.isFunction(filter) ) {
						if ( ! filter.call(this, elemp) ){
							continue;
						}
					}else if(filter === 'NO_OPTION_UNSELECTED'){
						if(elemp.tagName == 'OPTION' && !elemp.selected){
							continue;
						}
					}else if(filter){
						if( ! $(elemp).is(filter)){
							continue;
						}
					}
				}
				if ( jQuery.isFunction(nodecallback) ) {
					ret += nodecallback.call(this, elem.nodeValue, elem, elemp);
				}else if(nodecallback === 'trimsep'){
					if($.trim(ret) !== ''){
						ret += ' ';
					}
					ret += $.trim(elem.nodeValue);
				}else if(nodecallback === 'trim'){
					ret += $.trim(elem.nodeValue);
				}else{
					ret += elem.nodeValue;
				}

			} else {
				ret += getTextFiltered( elem.childNodes , filter, nodecallback );
			}
		}

		return ret;
	};

	$.textFiltered = getTextFiltered;

	jQuery.fn.extend({
		textFiltered: function( filter, nodecallback )...