tag:blogger.com,1999:blog-34747480808147755822018-08-14T14:57:56.274-05:00Javascript, jQuery and other JunkThis blog contains my random dabbling in site design and improvement. Please feel free to browse and use anything on this site. All I ask is you give me some feedback or even advice on how to improve or change anything.Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.comBlogger59125tag:blogger.com,1999:blog-3474748080814775582.post-67556881719273350512018-08-14T14:57:00.002-05:002018-08-14T14:57:56.155-05:00Git untagIf you've ever forgotten to rebase your local repository before pushing a tagged commit to master <pre><code>git push --tags origin master</code></pre> you'll know the pain of having the push rejected with the tag still applied remotely. In <a href="">this post by Nathan Hoad</a>, you'll find the needed git commands to remove the remote tag, but it's not <em>that</em> easy to remember. <pre>git tag -d 12345<br />git push origin :refs/tags/12345</pre> <p>So we're going to make a nice little shell command to do it for you!</p> <p>First open the global user defined <code>.gitconfig</code> file. From the command line:</p> <ul><li>You can either find the user <code>.gitconfig</code> file location by using <code>git config --list --show-origin</code> and finding it in the wall of text. Then, open the file in your favorite editor, <em>or</em></li><li>To load the file directly into your favorite editor, use <code>~/.gitconfig</code> as the path to the config file. For example, if you have <a href="https://atom.io/">Atom</a> installed, use <code>atom ~/.gitconfig</code>.</li></ul> <p>Now under the <code>[alias]</code> block (add it if you don't see it), add the untag alias:</p> <pre>[alias]<br /> untag = "!sh -c 'git tag -d $0 && git push origin :refs/tags/$0'"</pre> <p>And you're done!</p> <p>To use the command, use the alias followed by the tag to delete & remove from the remote branch</p> <pre>git untag v1.0.0</pre> You can find a lot more useful git aliases from the <a href="https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases">Git basics documentation</a>.Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-51542594108764784022017-05-11T18:44:00.001-05:002017-05-11T19:05:13.898-05:00Convert RGB(A) output to Hex ColorUpdate from my <a href="http://wowmotty.blogspot.com/2009/06/convert-jquery-rgb-output-to-hex-color.html">Convert jQuery RGB output to Hex Color</a> Post Modern browsers will soon support <a href="http://caniuse.com/#feat=css-rrggbbaa">four and eight-digit hex colors</a>, which means you now include an alpha channel with your color. As you can see in the comments of <a href="https://css-tricks.com/8-digit-hex-codes/">this CSS Tricks post</a>, the alpha channel conversion is different from the other color channels and this makes it very confusing to convert: <ul><li>The <code>r</code> (red), <code>g</code> (green), and <code>b</code> (blue) channels have decimal values ranging from 0 to 255.</li><li>the <code>a</code> (alpha) channel has a decimal value that ranges from 0 to 100.</li></ul> My old post <a href="http://wowmotty.blogspot.com/2009/06/convert-jquery-rgb-output-to-hex-color.html">Convert jQuery RGB output to Hex Color</a> was written to ignore the alpha channel when entering an RGBA value. This post updates both the javascript and the demo to output an <code>#RRGGBB</code> or <code>#RRGGBBAA</code> hex code value. <p></p><iframe width="100%" height="300" src="//jsfiddle.net/Mottie/don375nj/embedded/result/dark/" allowfullscreen="allowfullscreen" frameborder="0"></iframe><p></p> <pre class="prettyprint lang-js"><br />function rgb2hex(orig) {<br /> var a, isPercent,<br /> rgb = orig.replace(/\s/g, '').match(/^rgba?\((\d+),(\d+),(\d+),?([^,\s)]+)?/i),<br /> alpha = (rgb && rgb[4] || "").trim(),<br /> hex = rgb ? "#" +<br /> (rgb[1] | 1 &lt;&lt; 8).toString(16).slice(1) +<br /> (rgb[2] | 1 &lt;&lt; 8).toString(16).slice(1) +<br /> (rgb[3] | 1 &lt;&lt; 8).toString(16).slice(1) : orig;<br /> if (alpha !== "") {<br /> isPercent = alpha.indexOf("%") > -1;<br /> a = parseFloat(alpha);<br /> if (!isPercent && a &gt;= 0 && a &lt;= 1) {<br /> a = Math.round(255 * a);<br /> } else if (isPercent && a &gt;= 0 && a &lt;= 100) {<br /> a = Math.round(255 * a / 100)<br /> } else {<br /> a = "";<br /> }<br /> }<br /> if (a) {<br /> hex += (a | 1 &lt;&lt; 8).toString(16).slice(1);<br /> }<br /> return hex;<br />}<br /></pre> I've copied some of the above code from the <a href="https://github.com/sindresorhus/rgb-hex">rgb-hex</a> node module, where I also contributed alpha channel support. Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-27547262901540319182014-04-03T11:35:00.000-05:002016-06-20T20:57:17.680-05:00Methods to add multi-line CSS contentI'm sure most of you know that you can add content before or after an element using css; And you can add a line break within that content (<a href="http://www.w3.org/TR/CSS2/generate.html#x18">spec</a>):<br /><br />HTML <br /><pre class="prettyprint lang-html">&lt;div id="test1"&gt;Hello World&lt;/div&gt;</pre><br />CSS <br /><pre class="prettyprint lang-css">#test1:after {<br /> content : ' of foo \A barred';<br /> white-space: pre; /* this line is essential */<br />}</pre><br />Sadly, this same method doesn't work if you get your content from an attribute: <br /><br />HTML <br /><pre class="prettyprint lang-html">&lt;div id="test1" data-extra=" of bar \A food"&gt;Hello World&lt;/div&gt;</pre><br />CSS <br /><pre class="prettyprint lang-css">#test1:after {<br /> content : attr(data-extra);<br /> white-space: pre;<br />}</pre><br />Example: <iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" src="https://jsfiddle.net/96pwE/embedded/result,html,css" width="100%"></iframe> So after some <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=988694">discussion</a> with the developers of Firefox, I learned that the following alternatives do work; make sure to set the css <code>white-space</code> to <code>pre</code>.<br><br>I was hoping that these methods would work consistently and I wouldn't have to remember, so I made this blog post to remind me :)<br><br>Here is a condensed list:<br><br /><table> <thead><tr><th style="width: 100px;">content source</th><th>Carriage Return</th><th>Example</th></tr></thead> <tbody><tr><td>inline string</td><td><pre class="prettyprint lang-js">\A</pre></td><td><pre class="prettyprint lang-css">content: "line1 \A line2"</pre></td></tr><tr><td>javascript</td><td><pre class="prettyprint lang-js">\n</pre></td><td><pre class="prettyprint lang-js">.setAttribute('data-extra', " line1 \n line2");</pre></td></tr><tr><td rowspan="3">data-attribute</td><td><code>inline carriage return</code></td><td><pre class="prettyprint lang-js">data-extra="line1<br /> line2"</pre></td></tr><tr><td><code>&amp;#10;</code></td><td><pre class="prettyprint lang-js">data-extra="line1 &amp;#10; line2"</pre></td></tr><tr><td><code>&amp;#xA;</code> (hex)</td><td><pre class="prettyprint lang-js">data-extra="line1 &amp;#xA; line2"</pre></td></tr></tbody></table><br>Here is a full example: <iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" src="https://jsfiddle.net/96pwE/5/embedded/result,html,js,css" width="100%"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-4357612120804635292014-01-30T10:15:00.000-06:002014-01-30T10:31:42.426-06:00Add Anchors to jQuery UI Accordion HeadersIf you want jQuery UI's accordion (version 1.10.0+) to be able to open the desired section based on the window hash, you are stuck with using a hash that looks something like this <a href="http://fiddle.jshell.net/Mottie/XF7DC/2/show/#ui-accordion-accordion-header-1">"#ui-accordion-accordion-header-1"</a>, and well, the panel won't open. It requires some extra coding.<br /><br />With this code you can add a link within the header which opens the panel and updates the hash. It also opens the panel based on the hash after the accordion initializes. <br />I originally answered this code on <a href="http://stackoverflow.com/a/1863106/145346">Stackoverflow</a>, but thought it would be nice to have a write up with demo.<br/> <pre class="prettyprint lang-js">var hashId = 0,<br /> $accordion = $('#accordion');<br />if (window.location.hash) {<br /> $accordion.children('h3').each(function (i) {<br /> var txt = this.textContent.toLowerCase().replace(/\s+/g, '_');<br /> if ( txt === window.location.hash.slice(1) ) {<br /> hashId = i;<br /> }<br /> });<br />}</pre> This above first block of code sets a default hash id (zero-based index of the currently open header), then looks within each accordion header's text and checks if it matches the current window hash. The line with <code>this.textContent</code> (may not work in older browsers, so change it to <code>$(this).text()</code> as needed), gets the header text and replaces any spaces, tabs, etc that don't work within an element ID. If the header text is really long, contains punctuation like commas, exclamation points, etc, then you may need to truncate the text with something like this: <pre class="prettyprint lang-js">var txt = $(this).text()<br /> .toLowerCase()<br /> .replace(/[^a-z\s]/g,'')<br /> .replace(/\s+/g, '_')<br /> .substring(0,10)</pre>This code replaces any text that isn't a letter in the alphabet, or a space, replaces spaces with an underscore, then saves the first 10 characters. <br/>Then it compares it to the window hash. The <code>.slice(1)</code> removes the hash ("#") before comparing it to the accordion header text.<br/>The hash id is then set using the header index.<br/><br/>Now we can initialize the accordion... <br /><pre class="prettyprint lang-js">$accordion.accordion({<br /> active: hashId,<br /> animate: false,<br /> heightStyle: 'content',<br /> collapsible: true,<br /> create: function (event, ui) {<br /> $accordion.children('h3').each(function (i) {<br /> // set id here because jQuery UI sets them as "ui-accordion-#-header-#"<br /> this.id = this.textContent.toLowerCase().replace(/\s+/g, '_');<br /> // add the anchor<br /> $(this).before('<a class="accordion-link link" data-index="' + i +<br /> '" href="#' + this.id + '"></a>');<br /> });<br /> $accordion.find('.accordion-link').click(function () {<br /> // the active option requires a numeric value (not a string, e.g. "1")<br /> $accordion.accordion( "option", "active", $(this).data('index') );<br /><br /> // uncomment out the return false below to prevent the header jump<br /> // return false;<br /> });<br /> }<br />});</pre><br />Set the <code>active</code> option to initialize the accordion with the hash tag matching section open, it uses a zero-based index.<br/><br/>The <code>animate</code> option is set to <code>false</code> because when the user clicks on a link, the page jumps to the section... then the animation opens the panel. It sometimes animates so the entire page scrolls up and the section header is no longer at the top of the page.<br/><br/>The <code>heightStyle</code> and <code>collapsible</code> options are set to my personal preference, change them as desired.<br/><br/>Now the <code>create</code> option needs to contain code to add the links and make them clickable. It cycles through all of the section headers, replaces the id to match the header text - use the code that was used to match the text in the first block of code, so the code will compare the text parsed in the same way - then add the link before the header. If the link is added after the header, the accordion messes up because it is set to look for the element (a <code>&lt;div&gt;</code>) immediately following the header. The link contains a "data-index" attribute which contains the header zero-based index used to set the active accordion panel.<br/><br/>And finally the code to make the link clickable. After setting the "active" accordion option, you can chose to <code>return false</code>, or not. If not included, the selected header will jump to the top of the browser page and if included, the page will not scroll.<br/><br/>Lastly, you'll need to include some css to position the link image within the section header: <pre class="prettyprint lang-css">.ui-accordion {<br /> position: relative;<br />}<br />.ui-accordion .accordion-link {<br /> position: absolute;<br /> right: 2%;<br /> margin-top: 16px; /* adjust as needed to vertically center the icon */<br /> z-index: 1;<br /> width: 12px; /* approx 12x12 link icon */<br /> height: 12px;<br /> background: url(http://i57.tinypic.com/fyfns4.png) center center no-repeat;<br />}</pre>Here is a demo of what it looks like: <iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" src="http://jsfiddle.net/Mottie/XF7DC/embedded/result" width="100%"></iframe> or, try <a href="http://fiddle.jshell.net/Mottie/XF7DC/show/#section_2">this full screen version of the same demo</a>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-6967532506853758862013-04-02T10:09:00.000-05:002014-01-30T10:33:26.464-06:00Regex Replacement String MathI needed a simple method to do math within a regex replacement. Basically, a simple way for someone using a plugin to choose between a zero or one based index.<br /><br />Yes, I could have just added an additional replacement like this:<br /><pre class="prettyprint lang-js">var string = '&amp;index0={index}&amp;index1={index+1}'<br /> .replace(/\{index\}/, index)<br /> .replace(/\{index\+1\}/, index + 1);<br /></pre><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" src="http://jsfiddle.net/Mottie/zdbYP/embedded/result,js" width="100%"></iframe> <br />That works! But what you if for some unknown reason needed the index to start at 2, 3 or even 10? Easy, go back in and modify the code.<br /><br />Or, just use some regex math that allows you to set any number. Try this demo, and change the last input string to use "{index+10}" or even "{index-5}": <br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" height="300" src="http://jsfiddle.net/Mottie/RtMdG/embedded/result,js" width="100%"></iframe> The code isn't really that complicated:<br /><pre class="prettyprint lang-js">var string = '&amp;index0={index}&amp;index1={index+1}'<br /> .replace(/\{index([-+]\d+)?\}/g, function(fullstring, match){<br /> return index + (match ? parseInt(match, 10) : 0);<br /> });<br /></pre>It uses a replace function to take the index and add the first regex matching string obtained from the <code>match</code> argument.<br /><br />But before that, we need to use a <a href="http://en.wikipedia.org/wiki/Conditional_(programming)#If_expressions">ternary operator</a> to check if there is a match. If there isn't one, then add zero to the index.<br /><br />We then need to parse the matched string into a numerical value so we can add it to the index. Then return the result.<br /><br />I hope someone finds this useful :)Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-10738980683099425182013-01-30T16:02:00.001-06:002014-01-18T18:14:19.120-06:00More fun with the base tag!I have a post from a few years ago about using <a href="http://wowmotty.blogspot.com/2011/05/base-tag.html">the base tag</a> for debugging. Well I just figured out an even cooler way to use it in jsFiddle demos!<br /><br />I have a base jsFiddle demo for just about every plugin I've made. It makes it easier for others to experiment with all of the plugin options to get a better feel for it. Well, at least I think so. At the very least, it makes it easier for me to build a demo to help all of you ;)<br /><br />Anyway, I wanted to link to the images from the AnythingSlider github repo. The urls end up being quite long: <a href="http://css-tricks.github.com/AnythingSlider/demos/images/slide-civil-1.jpg">http://css-tricks.github.com/AnythingSlider/demos/images/slide-civil-1.jpg</a><br /><br />Enter the base tag! I just add it to the top of the html frame, and BAM short url sweetness inside :P<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/Mottie/ycUB6/4045/embedded/html,js,result" style="height: 300px; width: 100%;"></iframe> <br /><br /><del>I tried making this same demo with <a href="http://codepen.io/Mottie/pen/xLAhe">codepen.io</a>, but sadly it didn't work =(</del><br />Update 1/18/2014: It works on <a href="http://codepen.io/Mottie/pen/smLrv">codepen.io</a> now! Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-73816529893471690302012-12-21T12:18:00.001-06:002014-01-30T10:35:07.938-06:00Using Google Closure Compiler to write better codeSo I ran into a bit of code that I wrote a while back. I thought could be written a lot more efficiently. So I thought, hey <a href="http://closure-compiler.appspot.com/home">Google's Closure Compiler</a> is good at that stuff right? Let see what happens!<br /><br />So, lets say we started out with this list of contacts:<br /><br /><pre class="prettyprint lang-js;">var list = {<br /> 'friends' : {<br /> 'fred' : '123-4567',<br /> 'lisa' : '123-6789'<br /> },<br /> 'work' : {<br /> 'joe' : '234-5678'<br /> }<br />}</pre><br />I need a function to add someone to the list, or update a number, but it need to check if the group already exists or if the person is already listed:<br /><br /><pre class="prettyprint lang-js;">function addName(group, name, number){<br /> if (list[group] &amp;&amp; list[group][name]){<br /> list[group][name] = number;<br /> } else {<br /> if (list[group]){<br /> list[group][name] = number;<br /> } else {<br /> list[group] = {};<br /> list[group][name] = number;<br /> }<br /> }<br />}</pre><br />so the <code>addName</code> function starts out by seeing if the group exists, then if the name exists within that group:<br /><br /><pre class="prettyprint lang-js;">if (list[group] &amp;&amp; list[group][name]){</pre><br />if that works, then just add/update the number. If not, then we drop through the <code>else</code> to the next set of evaluations. Does the group exist?<br /><br /><pre class="prettyprint lang-js;">if (list[group]){</pre><br />Yes? Add the number, if not, keep going to through the next <code>else</code>, from here we know that the group doesn't exist, so we define it, then add the name.<br /><br />Seems&nbsp;efficient&nbsp;right?... hmm, but I see a lot of <code>list[group][name] = number;</code> in there. Lets see what Google's closure compiler does to it.<br /><br />But first we need to keep the compiler from changing all the names. Lets pull out the code from within the function and force the compiler to keep our constants (see the first line):<br /><br /><pre class="prettyprint lang-js;">/** @const */ var list = {}, group, name, number;<br /><br /> if (list[group] &amp;&amp; list[group][name]){<br /> list[group][name] = number;<br /> } else {<br /> if (list[group]){<br /> list[group][name] = number;<br /> } else {<br /> list[group] = {};<br /> list[group][name] = number;<br /> }<br /> }</pre><br />The extra asterisk in the comment is important! (<a href="https://developers.google.com/closure/compiler/docs/js-for-compiler">ref</a>). Note, maybe there is a better way to force the compiler to keep your variable names within a function, but I don't know it.<br /><br />Anyway, click the reset link and paste the code above into the <a href="http://closure-compiler.appspot.com/home">compiler</a> (use the "Simple" setting if it isn't already set). You'll end up with this:<br /><br /><pre class="prettyprint lang-js;">var list={},group,name,number;if(!list[group]||!list[group][name])list[group]||(list[group]={});list[group][name]=number;</pre><br />If it's too messy for you to clean up yourself, or you have a lot more code, copy that result and paste it into <a href="http://jsbeautifier.org/">jsbeautifier</a>&nbsp;and you'll end up with nicer formatted code.<br /><br />Also, it's important to remember that compilers don't necessarily always use curly brackets after <code>if</code> or <code>else</code> statements, so the above code is equivalent to:<br /><br /><pre class="prettyprint lang-js;">var list = {}, group, name, number;<br />if (!list[group] || !list[group][name]) {<br /> list[group] || (list[group] = {});<br />}<br />list[group][name] = number;</pre><br />So now we can replace our function wrapper and remove the constants:<br /><br /><pre class="prettyprint lang-js;">function addName(group, name, number){<br /> if (!list[group] || !list[group][name]) {<br /> list[group] || (list[group] = {});<br /> }<br /> list[group][name] = number;<br />}</pre><br />That looks better, but I'm still seeing a bunch of <code>list[group]</code> in there. I'm sure the code can be a bit more DRY (<a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">don't repeat yourself</a>). Also do we really need to check <code>list[group][name]</code>? It was checked initially but not checked again...<br /><br /><pre class="prettyprint lang-js;">function addName(group, name, number){<br /> if (!list[group]) {<br /> list[group] || (list[group] = {});<br /> }<br /> list[group][name] = number;<br />}</pre><br />Well now I see that <code>list[group]</code> is checked twice. Let's get rid of that and see what happens:<br /><br /><pre class="prettyprint lang-js;">function addName(group, name, number){<br /> if (!list[group]) {<br /> list[group] = {};<br /> }<br /> list[group][name] = number;<br />}</pre><br />Now lets see what we have. Check if <code>list[group]</code> exists, then add it if it doesn't. Add the number. Wow! Short and sweet. Why didn't I write it like that originally? Oh yeah, I'm still a noob :)<br /><br />So after some testing, this new cleaned up function works exactly like the original! We went from 10 lines inside of the <code>addName</code> function down to 4! I'm happy, a happy noob!<br /><br />I threw together this demo for testing.<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/Mottie/y4tyY/embedded/result,js,html" style="height: 300px; width: 100%;"></iframe><br /><br />I hope that my process of learning at least gives you some ideas. I don't claim to be an expert at javascript or jQuery, but I try to learn a little every day :)<br /><br />As a redneck friend of mine once said,&nbsp;"Dem Google people is smart!"Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-27942691180071125342012-08-02T11:19:00.000-05:002012-08-26T18:31:53.278-05:00CSS-Tricks OrganizationIf you follow me on Github, you may notice that some of my repositories are now missing... Well, I moved them over to the CSS-Tricks Organization repositories. I thought it would make it easier for people to find and contribute to them, especially if I'm away on vacation ;)<br /><br />All of the following repos have been transferred, so far:<br /><br /><ul><li><a href="https://github.com/CSS-Tricks/AnythingZoomer">AnythingZoomer</a></li><li><a href="https://github.com/CSS-Tricks/AnythingSlider-Themes">AnythingSlider-Themes</a></li><li><a href="https://github.com/CSS-Tricks/AnythingSlider-Fx-Builder">AnythingSlider-Fx-Builder</a></li><li><a href="https://github.com/CSS-Tricks/mod_anythingslider">mod_anythingslider</a> (for Joomla)</li><li><a href="https://github.com/CSS-Tricks/Equalizer">Equalizer</a></li><li><a href="https://github.com/CSS-Tricks/MovingBoxes">MovingBoxes</a></li></ul><div>I hope this move get more feedback and contributions to improve the code even more :)</div><div><br /></div><div>And no, I'm not abandoning any of them.</div>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-35189218022780065282012-07-23T16:19:00.001-05:002014-01-30T10:36:38.287-06:00jQuery unwrapInner()Update: Well, I made a nice post about extending jQuery with an unwrapInner function, but I discovered that the same thing can be done by using jQuery's <a href="http://api.jquery.com/replaceWith/"><code>replaceWith()</code></a> function... <br>Starting with this HTML: <pre class="prettyprint lang-js; html-script: true">&lt;div class=&quot;test&quot;&gt;<br /> &lt;span class=&quot;red&quot;&gt;<br /> red text<br /> &lt;/span&gt;<br /> &lt;div class=&quot;green&quot;&gt;<br /> green text<br /> &lt;/div&gt;<br />&lt;/div&gt;<br /></pre> the span from around the red text can be removed by using the <code>replaceWith()</code> function as follows: <pre class="prettyprint lang-js">// unwrap red text<br />$('.test').find('.red')<br /> .replaceWith( $('.test .red').contents() );<br /><br /></pre> So you can pretty much ignore the rest of this post =( <hr><br>jQuery has a wrap and wrapAll which can be removed by using the unwrap function. But it only targets the parents of the selected element. What about wrapInner? It doesn't have a method to remove it. So I put together this small extension which does just that.<br /><br />So say we start with this HTML: <br /><pre class="prettyprint lang-js; html-script: true">&lt;div class="test"&gt;<br /> &lt;span&gt;some text&lt;/span&gt;<br />&lt;/div&gt;<br /></pre> To remove that span from around "some text", we just call the unwrapInner code below <br /><pre class="prettyprint lang-js">// unwrapInner function<br />jQuery.fn.extend({<br /> unwrapInner: function(selector) {<br /> return this.each(function() {<br /> var t = this,<br /> c = $(t).children(selector);<br /> if (c.length === 1) {<br /> c.contents().appendTo(t);<br /> c.remove();<br /> }<br /> });<br /> }<br />});<br /></pre> You can include a selector to target a specific element<br> <pre class="prettyprint lang-js">// examples<br />$("div").unwrapInner(); // remove immediate child<br />$("div").unwrapInner("span"); // remove immediate child if it's a span<br />$("div").unwrapInner("span.inner"); // remove all immediate children of class '.inner'<br /></pre> Note that the one issue with this function is when there are multiple children to be unwrapped, it will add the unwrapped content to the end. For example, with this HTML:<br> <pre class="prettyprint lang-js; html-script: true">&lt;div class=&quot;test&quot;&gt;<br /> &lt;span class=&quot;red&quot;&gt;<br /> red text<br /> &lt;/span&gt;<br /> &lt;div class=&quot;green&quot;&gt;<br /> green text<br /> &lt;/div&gt;<br />&lt;/div&gt;<br /></pre> We want to unwrap <code>span.red</code>, so we use <pre class="prettyprint lang-js">$(".test").unwrapInner(".red");<br /></pre> but we get this result: <pre class="prettyprint lang-js; html-script: true">&lt;div class=&quot;test&quot;&gt;<br /> &lt;div class=&quot;green&quot;&gt;<br /> green text<br /> &lt;/div&gt;<br /> red text<br />&lt;/div&gt;<br /></pre> Check out this demo: <iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/Mottie/MsEz7/1/embedded/result,js,html" allowfullscreen="allowfullscreen" frameborder="0"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com4tag:blogger.com,1999:blog-3474748080814775582.post-446548242394657192011-12-30T17:12:00.002-06:002014-01-30T10:38:54.060-06:00Adding the MovingBoxes plugin to BloggerThese instructions will allow you to add a MovingBoxes widget to any Blogger post. These instruction really apply to any plugin out there, but instead of making a generic post, I think a specific example is best.<br /><br /><ul class="movingboxes"><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/1.jpg" /><br /><h3>Olivia News Heading</h3>A very short exerpt goes here... <a href="http://flickr.com/photos/justbcuz/112479862/">more</a></li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/2.jpg" /><br /><h3>Alice News Heading</h3>A very short exerpt goes here... <a href="http://flickr.com/photos/joshuacraig/2698975899/">more</a> and a whole lot more text goes here, so we can see the height adjust.</li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/3.jpg" /><br /><h3>Yin News Heading</h3>A very short exerpt goes here... <a href="http://flickr.com/photos/ruudvanleeuwen/468309897/">more</a></li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/4.jpg" /><br /><h3>Gerri News Heading</h3>A very short exerpt goes here... <a href="http://flickr.com/photos/emikohime/294092478/">more</a></li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/5.jpg" /><br /><h3>Tabitha News Heading</h3>A very short exerpt goes here... <a href="http://www.flickr.com/photos/fensterbme/499006584/">more</a></li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/6.jpg" /><br /><h3>Mary News Heading</h3>A very short exerpt goes here... <a href="http://www.blogger.com/post-create.g?blogID=3474748080814775582#">more</a></li><li><img alt="picture" src="http://css-tricks.github.com/MovingBoxes/demo/7.jpg" /><br /><h3>Kitty News Heading</h3>A very short exerpt goes here... <a href="http://www.blogger.com/post-create.g?blogID=3474748080814775582#">more</a></li></ul><br /><b>Note: In the next version of MovingBoxes, I plan to change the width and panelWidth options. At that time, I will update this post.</b><br /><br /><b><span style="font-size: large;">Code, CSS and image setup</span></b><br /><br /><b>Required Files</b><br />I don't know how github would feel about directly linking to their servers. Most businesses frown upon hot-linking and will most likely block your URL, or the entire domain if the hot-linking gets too rampant. So I'd recommend copying each file below and saving it to your own server.<br /><ul><li><a href="http://css-tricks.github.com/MovingBoxes/css/movingboxes.css">http://css-tricks.github.com/MovingBoxes/css/movingboxes.css</a>&nbsp;(don't copy this one, see below)</li><li><a href="http://css-tricks.github.com/MovingBoxes/images/arrows.png">http://css-tricks.github.com/MovingBoxes/images/arrows.png</a> </li><li><a href="http://css-tricks.github.com/MovingBoxes/js/jquery.movingboxes.min.js">http://css-tricks.github.com/MovingBoxes/js/jquery.movingboxes.min.js</a></li></ul>Note that inside of the css, you'll need to change the arrows.png url to point to the new location of the image file.<br /><br /><pre class="prettyprint lang-css">/*** Left &amp; Right Navigation Arrows ***/<br />a.mb-scrollButtons {<br /> background: transparent url(../images/arrows.png) no-repeat;<br />}</pre><br />If you don't have a server, you can use <a href="https://www.dropbox.com/">dropbox</a> or your iCloud (I don't own a Mac, so I'm only guessing that it'll allow you to save javascript &amp; css files). If you don't have a server to save the files, then first, save the image to an image hosting site, like <a href="http://photobucket.com/">photobucket</a>, <a href="http://www.flickr.com/">Flickr</a> or something. Then you can save the javascript and css into your own blog design. The main problem with saving into your page design is that it will&nbsp;increase your page size and possibly make the page loading take longer.&nbsp;Either way you'll need to add a gadget, or add to an already existing one.<br /><br /><b>Add a Gadget</b><br />So once you have your files saved and the css file updated:<br /><ul><li>Go to your Dashboard &gt; Design &gt; Add a Gadget &gt; Pick the "HTML/Javascript" gadget.<br /></li><li>Once the popup window opens, add the following plugin css and javascript in the content window, the URL should point to your hosted files</li><li>Note: After testing this, it seems that adding a &lt;link&gt; tag&nbsp;inside of the HTML/Javascript gadget gets removed. I'm not sure why, but in this case we can just paste in the entire css.<br /><br /><pre class="prettyprint lang-html">&lt;style&gt;<br />/*** Overall MovingBoxes Slider ***/<br />.mb-wrapper {<br /> width: 900px; /* default, this is overridden by script settings */<br /> min-height: 200px;<br /> border: 5px solid #ccc;<br /> margin: 0 auto;<br /> position: relative;<br /> left: 0;<br /> top: 0;<br /> border-radius: 1em;<br /> -moz-border-radius: 1em;<br /> -webkit-border-radius: 1em;<br /> box-shadow: inset 0 0 10px #888;<br /> -moz-box-shadow: inset 0 0 10px #888;<br /> -webkit-box-shadow: inset 0 0 10px #888;<br />}<br /><br />/* Panel Wrapper */<br />.mb-slider, .mb-scroll {<br /> width: 100%;<br /> height: 100%;<br /> overflow: hidden;<br /> margin: 0 auto;<br /> padding: 0;<br /> position: relative;<br /> left: 0;<br /> top: 0;<br /><br /> /***(&gt;'-')&gt; Control Panel Font size here &lt;('-'&lt;)***/<br /> font-size: 18px;<br />}<br /><br />/* active slider border highlight */<br />.mb-active-slider {<br /> border-color: #999bff;<br />}<br /><br />/*** Slider panel ***/<br />.mb-slider .mb-panel {<br /> width: 350px; /* default, this is overridden by script settings */<br /> margin: 5px 0;<br /> padding: 5px;<br /> display: block;<br /> cursor: pointer;<br /> float: left;<br /> list-style: none;<br />}<br /><br />/* Cursor to arrow over current panel, pointer for all others,<br />change .current class name using plugin option, currentPanel : 'current' */<br />.mb-slider .mb-panel.current {<br /> cursor: auto;<br />}<br /><br />/*** Inside the panel ***/<br />.mb-inside {<br /> padding: 10px;<br /> border: 1px solid #999;<br />}<br /><br />.mb-inside * {<br /> max-width: 100%;<br />}<br /><br />/*** Left &amp; Right Navigation Arrows ***/<br />a.mb-scrollButtons {<br /> display: block;<br /> width: 45px;<br /> height: 58px;<br /> background: transparent url(http://css-tricks.github.com/MovingBoxes/images/arrows.png) no-repeat;<br /> position: absolute;<br /> top: 50%;<br /> margin-top: -29px; /* if you change the arrow images, you may have to adjust this (1/2 height of arrow image) */<br /> cursor: pointer;<br /> text-decoration: none;<br /> outline: 0;<br /> border: 0;<br />}<br />a.mb-scrollButtons.mb-left {<br /> background-position: left top;<br /> left: -45px;<br />}<br />a.mb-scrollButtons.mb-right {<br /> background-position: right top;<br /> right: -45px;<br />}<br />a.mb-scrollButtons.mb-left:hover {<br /> background-position: left bottom;<br />}<br />a.mb-scrollButtons.mb-right:hover {<br /> background-position: right bottom;<br />}<br />a.mb-scrollButtons.disabled {<br /> display: none;<br />}<br /><br />/*** Controls added below the panels ***/<br />.mb-controls {<br /> margin: 0 auto;<br /> text-align: center;<br /> background: #ccc;<br /> position: relative;<br /> z-index: 100;<br />}<br />.mb-controls a {<br /> color: #444;<br /> font: 12px Georgia, Serif;<br /> display: inline-block;<br /> text-decoration: none;<br /> padding: 2px;<br /> height: 18px;<br /> margin: 0 5px 0 0;<br /> text-align: center;<br /> outline: 0;<br />}<br />.mb-controls a.current, .mb-controls a:hover {<br /> color: #fff;<br />}<br />.mb-active-slider .mb-controls {<br /> background: #999bff;<br />}<br />&lt;/style&gt;<br />&lt;script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"&gt;&lt;/script&gt;<br />&lt;script src="http://css-tricks.github.com/MovingBoxes/js/jquery.movingboxes.min.js"&gt;&lt;/script&gt;<br />&lt;script&gt;<br />jQuery(function(){<br /><br /> jQuery('.movingboxes').movingBoxes({<br /> startPanel : 1, // start with this panel<br /> width : 300, // overall width of movingBoxes<br /> panelWidth : 0.5, // current panel width adjusted to 50% of overall width<br /> wrap : false, // if true, the panel will "wrap" (it really rewinds/fast forwards) at the ends<br /> buildNav : true, // if true, navigation links will be added<br /> navFormatter : function(){ return "&amp;#9679;"; } // function which returns the navigation text for each panel<br /> });<br /><br />});<br />&lt;/script&gt;</pre><br /></li><li>Modify the "width" and "panelWidth" options (in <span style="color: red;">red</span>) as desired. Or add and/or remove options following <a href="https://github.com/chriscoyier/MovingBoxes/wiki/Setup">these instructions</a>.<br /></li><li>If you don't have your own server, then you can open the movingboxes.css file in a text editor and save it into the content window; again make sure the arrow.png url is pointing to the new location in the css below.<br /><br /><pre class="prettyprint lang-html">&lt;style&gt;<br /> /* add movingboxes.css contents here */<br />&lt;/style&gt;<br />&lt;script&gt;<br /> /* add jquery.movingboxes.min.js contents here */<br />&lt;/script&gt;</pre></li></ul><b><span style="font-size: large;">Adding a blog post</span></b><br /><br />Now the first step here would be to open a new post.<br /><br />Switch the editor to "Edit HTML" - tab in the upper right corner, then paste in the basic plugin HTML Note, keep the&nbsp;&lt;img&gt;&nbsp;and&nbsp;&lt;li&gt;&nbsp;on the same line or blogger will add a&nbsp;&lt;br&gt;&nbsp;in between.<br /><br /><pre class="prettyprint lang-html">&lt;ul class="movingboxes"&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/1.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Olivia News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="http://flickr.com/photos/justbcuz/112479862/"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/2.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Alice News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="http://flickr.com/photos/joshuacraig/2698975899/"&gt;more&lt;/a&gt; and a whole lot more text goes here, so we can see the height adjust.&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/3.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Yin News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="http://flickr.com/photos/ruudvanleeuwen/468309897/"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/4.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Gerri News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="http://flickr.com/photos/emikohime/294092478/"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/5.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Tabitha News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="http://www.flickr.com/photos/fensterbme/499006584/"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/6.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Mary News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="#"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br /> &lt;li&gt;&lt;img src="http://css-tricks.github.com/MovingBoxes/demo/7.jpg" alt="picture"&gt;<br /> &lt;h2&gt;Kitty News Heading&lt;/h2&gt;<br /> &lt;p&gt;A very short exerpt goes here... &lt;a href="#"&gt;more&lt;/a&gt;&lt;/p&gt;<br /> &lt;/li&gt;<br />&lt;/ul&gt;</pre><br />Note that the UL has a class named "movingboxes", it originally had an ID, but to make this plugin work on any future blog entries we need to make sure it's a class name we can remember.<br /><br />Now, just publish your post and reload your page. The MovingBoxes plugin should initialize and look like it does at the top of this post :)Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com1tag:blogger.com,1999:blog-3474748080814775582.post-14874178739293461372011-12-17T13:29:00.002-06:002014-01-30T10:39:36.055-06:00All Images Loaded (imagesLoaded)<b>*NOTE*</b> this code causes infinite loops in IE with image load errors, so I improved and turned this into a real plugin and hosted it <a href="https://github.com/Mottie/imagesLoaded">on github</a>. <br /><br /><hr />A little jQuery function that checks if all images are loaded. If they all are, the callback function is called. Check out the latest version in this <a href="https://gist.github.com/1491097">gist</a>.<br /><br />Script below:<br /><pre class="prettyprint lang-js">/*<br /> Check if all images are loaded<br /> - Callback occurs when all images are loaded<br /> - image load errors are ignored (complete will be true)<br /> - Use:<br /> $('.wrap img').imagesLoaded(function(){<br /> alert('all images loaded');<br /> });<br />*/<br /><br />jQuery.fn.extend({<br /> imagesLoaded: function( callback ) {<br /> var i, c = true, t = this, l = t.length;<br /> for ( i = 0; i &lt; l; i++ ) {<br /> if (this[i].tagName === "IMG") {<br /> c = (c &amp;&amp; this[i].complete &amp;&amp; this[i].height !== 0);<br /> }<br /> }<br /> if (c) {<br /> if (typeof callback === "function") { callback(); }<br /> } else {<br /> setTimeout(function(){<br /> jQuery(t).imagesLoaded( callback );<br /> }, 200);<br /> }<br /> }<br />});<br /></pre><br />Use it as follows:<br /><pre class="prettyprint lang-js">$(function(){<br /> $('.wrap img').imagesLoaded(function(){<br /> alert('all images loaded');<br /> });<br />});</pre>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com2tag:blogger.com,1999:blog-3474748080814775582.post-72921954218367437612011-12-06T16:06:00.002-06:002011-12-06T16:08:20.387-06:00jQuery PathsliderI just created this plugin that is a very basic UI Slider (similar to the <a href="http://jqueryui.com/demos/slider/">jQuery slider</a>), but it doesn't just allow you to move the handle horizontally and vertically, it will follow any shaped curve! If you've ever used Adobe Illustrator or CorelDraw then you probably recognize the Bezier curve below.<br /><br />The handle, or grip as I call it, can be dragged along the black curve. For now, it only returns values from 0 (green dot) to 100 (red dot).<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://mottie.github.com/Pathslider/builder.html" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://i201.photobucket.com/albums/aa236/Mottie1/testsite/forums/pathslider.jpg" /></a></div><br />The plugin is in the early stages of development and still needs a lot of work, but it is usable now.<br /><br />Check out the <a href="http://mottie.github.com/Pathslider/index.html">demo page</a>, and the <a href="http://mottie.github.com/Pathslider/builder.html">builder page</a>. If you would like to help me out, submit an <a href="https://github.com/Mottie/Pathslider/issues">enhancement or issue</a>, or fork a copy of the plugin on <a href="https://github.com/Mottie/Pathslider">Github</a> and send me a pull request! I'd love the input!Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-72977390453991840612011-11-14T04:46:00.000-06:002011-11-14T04:46:22.448-06:00jQuery UI Side Scroller with buttonsOnce again, I got up at 2:30am wide awake... that's what happens when I fall asleep at 9:30 LOL. Anyway, I was bored so I searched for something to do. I found a <a href="http://css-tricks.com/forums/discussion/14542/cycle-through-images-with-a-custom-scroll-bar/p1">question on CSS Tricks</a> asking how to add buttons to the jQuery UI Slider. So starting with the <a href="http://jqueryui.com/demos/slider/#side-scroll">side scroll demo</a> as a base, I put together this demo. That is all :P<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/Mottie/TrQLK/1/embedded/result,js,html,css" style="height: 300px; width: 100%;"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-70123988046137843062011-10-31T19:21:00.001-05:002014-01-30T10:40:23.423-06:00Get All indexOf From An Array - Array.allIndexOf()This array function is an extension of Array.indexOf(), if it exists, and will return all indexes of the search element. It's named allIndexOf(). This code extends javascript and does not require jQuery. It is designed to work in older versions of IE as well, albeit a tiny bit slower.<br /><br />Originally if you wanted the second instance of a search element, you'd have to call "indexOf" twice. The second time with the starting index of the first result (plus one), or once with a guessed starting index. This should simplify the process for getting any or all of the indexes.<br /><br /><pre class="prettyprint lang-js">/*<br />Array.allIndexOf(searchElement)<br /> Array [Array] - the array to search within for the searchElement<br /> searchElement [String] - the desired element with which to find starting indexes<br />*/<br />(function(){<br /> Array.prototype.allIndexOf = function(searchElement) {<br /> if (this === null) { return [-1]; }<br /> var len = this.length,<br /> hasIndexOf = Array.prototype.indexOf, // you know, because of IE<br /> i = (hasIndexOf) ? this.indexOf(searchElement) : 0,<br /> n,<br /> indx = 0,<br /> result = [];<br /> if (len === 0 || i === -1) { return [-1]; }<br /> if (hasIndexOf) {<br /> // Array.indexOf does exist<br /> for (n = 0; n &lt;= len; n++) {<br /> i = this.indexOf(searchElement, indx);<br /> if (i !== -1) {<br /> indx = i + 1;<br /> result.push(i);<br /> } else {<br /> return result;<br /> }<br /> }<br /> return result;<br /> } else {<br /> // Array.indexOf doesn't exist<br /> for (n = 0; n &lt;= len; n++) {<br /> if (this[n] === searchElement) {<br /> result.push(n);<br /> }<br /> }<br /> return (result.length &gt; 0) ? result : [-1];<br /> }<br /> };<br />})();</pre>Use it as follows: <br /><pre class="prettyprint lang-js">var s = ["red","green","blue","red","yellow","blue","green","purple","red"];<br />s.allIndexOf("r"); // result [ -1 ]<br />s.allIndexOf("red"); // result [ 0,3,8 ]<br />s.allIndexOf("blue"); // result [ 2,5 ]</pre>Try out your own strings in the demo below or <a href="http://jsfiddle.net/Mottie/NneeH/2/embedded/result/">full screen</a>.<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/Mottie/NneeH/2/embedded/result,js,html,css" style="height: 300px; width: 100%;"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-21240760442483461112011-10-31T11:26:00.009-05:002014-01-30T10:40:42.602-06:00Get All indexOf From A Search String - String.allIndexOf()This string function is an extension of String.indexOf() and will return all indexes of a search string. It's named allIndexOf. This code extends javascript and does not require jQuery.<br /><br />Originally if you wanted the second instance of a search string, you'd have to call "indexOf" twice. The second time with the starting index of the first result (plus one), or once with a guessed starting index. This should simplify the process for getting any or all of the indexes. And yes, I woke up with a strange urge to write this function LOL.<br /><br /><pre class="prettyprint lang-js">/*<br />String.allIndexOf(searchstring, ignoreCase)<br /> String [String] - the string to search within for the searchstring<br /> searchstring [String] - the desired string with which to find starting indexes<br /> ignoreCase [Boolean] - set to true to make both the string and searchstring case insensitive<br />*/<br />(function(){<br /> String.prototype.allIndexOf = function(string, ignoreCase) {<br /> if (this === null) { return [-1]; }<br /> var t = (ignoreCase) ? this.toLowerCase() : this,<br /> s = (ignoreCase) ? string.toString().toLowerCase() : string.toString(),<br /> i = this.indexOf(s),<br /> len = this.length,<br /> n,<br /> indx = 0,<br /> result = [];<br /> if (len === 0 || i === -1) { return [i]; } // "".indexOf("") is 0<br /> for (n = 0; n &lt;= len; n++) {<br /> i = t.indexOf(s, indx);<br /> if (i !== -1) {<br /> indx = i + 1;<br /> result.push(i);<br /> } else {<br /> return result;<br /> }<br /> }<br /> return result;<br /> }<br />})();</pre>Use it as follows: <br /><pre class="prettyprint lang-js">var s = "The rain in Spain stays mainly in the plain";<br />s.allIndexOf("ain"); // result [ 5,14,25,40 ]<br />s.allIndexOf("the"); // result [ 34 ]<br />s.allIndexOf("THE", true); // result [ 0,34 ]</pre>Try out your own strings in the demo below or <a href="http://jsfiddle.net/Mottie/NneeH/1/embedded/result/">full screen</a>.<br /><br /><iframe allowfullscreen="allowfullscreen" frameborder="0" src="http://jsfiddle.net/Mottie/NneeH/1/embedded/result,js,html,css" style="height: 300px; width: 100%;"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-68032527099591561232011-10-20T09:20:00.004-05:002014-01-30T10:41:29.296-06:00jQuery Rename Attribute (renameAttr)I woke up this morning with a very unusual urge to make a rename attribute function for jQuery. I know, right.. that's just too weird! So after messing around with it, I decided that it needed to update the data as well. Here is the code I ended up with:<br /><br /><pre class="prettyprint lang-js">jQuery.fn.extend({<br /> renameAttr: function( name, newName, removeData ) {<br /> var val;<br /> return this.each(function() {<br /> val = jQuery.attr( this, name );<br /> jQuery.attr( this, newName, val );<br /> jQuery.removeAttr( this, name );<br /> // remove original data<br /> if (removeData !== false){<br /> jQuery.removeData( this, name.replace('data-','') );<br /> }<br /> });<br /> }<br />});</pre><br />Use it as follows:<br /><pre class="prettyprint lang-js">// $(selector).renameAttr(original-attr, new-attr, removeData);<br /><br />// removeData flag is true by default<br />$('#test').renameAttr('data-test', 'data-new' );<br /><br />// removeData flag set to false will not remove the<br />// .data("test") value<br />$('#test').renameAttr('data-test', 'data-new', false );<br /></pre><br />Basically, it adds a new attribute with the same value and removes the old one. It automatically removes the jQuery data for the original attribute unless you set the "removeData" flag to false. Check out this demo<br /><br /><iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/Mottie/Yjac5/embedded/result,js,html,css"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com1tag:blogger.com,1999:blog-3474748080814775582.post-62742331382077801772011-10-06T10:28:00.002-05:002014-01-30T10:42:11.307-06:00Adding Swipe SupportI spent some time trying to figure out how to add mobile swipe support without using jQuery Mobile, because the swipe is all I really needed. I don't own an iPhone/iPad/iAnything, so it took a bit of back-and-forth testing - thanks to all the folks over at <a href="http://css-tricks.com/forums/discussions">CSS-Tricks.com</a> - to get this to finally work.<br /><br /><iframe src="http://jsfiddle.net/Mottie/4eEA9/3/embedded/result,js,html,css" style="height: 300px; width: 100%;"></iframe><br /><br />Here is the complete code:<br /><br /><pre class="prettyprint lang-js">var maxTime = 1000, // allow movement if &lt; 1000 ms (1 sec)<br /> maxDistance = 50, // swipe movement of 50 pixels triggers the swipe<br /><br /> target = $('#box'),<br /> startX = 0,<br /> startTime = 0,<br /> touch = "ontouchend" in document,<br /> startEvent = (touch) ? 'touchstart' : 'mousedown',<br /> moveEvent = (touch) ? 'touchmove' : 'mousemove',<br /> endEvent = (touch) ? 'touchend' : 'mouseup';<br /><br />target<br /> .bind(startEvent, function(e){<br /> // prevent image drag (Firefox)<br /> e.preventDefault();<br /> startTime = e.timeStamp;<br /> startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;<br /> })<br /> .bind(endEvent, function(e){<br /> startTime = 0;<br /> startX = 0;<br /> })<br /> .bind(moveEvent, function(e){<br /> e.preventDefault();<br /> var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,<br /> currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),<br /> // allow if movement &lt; 1 sec<br /> currentTime = e.timeStamp;<br /> if (startTime !== 0 &amp;&amp; currentTime - startTime &lt; maxTime &amp;&amp; currentDistance &gt; maxDistance) {<br /> if (currentX &lt; startX) {<br /> // swipe left code here<br /> target.find('h1').html('Swipe Left').fadeIn();<br /> setTimeout(function(){<br /> target.find('h1').fadeOut();<br /> }, 1000);<br /> }<br /> if (currentX &gt; startX) {<br /> // swipe right code here<br /> target.find('h1').html('Swipe Right').fadeIn();<br /> setTimeout(function(){<br /> target.find('h1').fadeOut();<br /> }, 1000);<br /> }<br /> startTime = 0;<br /> startX = 0;<br /> }<br /> });<br /></pre><br /><br />Now, lets break down what each line does. First, some definitions:<br /><br /><pre class="prettyprint lang-js">var maxTime = 1000, // allow movement if &lt; 1000 ms (1 sec)<br /> maxDistance = 50, // swipe movement of 50 pixels triggers the swipe<br /><br /> target = $('#box'),<br /> startX = 0,<br /> startTime = 0,<br /></pre><br />The "maxTime" variable is a set to 1000 milliseconds. It is used to compare the touch start and touch end times, and if less than one second (1000 ms) it is considered to be a valid swipe, otherwise it is ignored. Basically, we don't like them slow swipers! Hehe, ok, actually, it to determine if the event ends up being a touch or a swipe.<br /><br />"maxDistance" is the number of pixels from the touch start point that the mouse or a finger moved to determine if a swipe or touch is occurring.<br /><br />The "target" variable, is the target element to add swipe support to. For the demo, I added an H1 tag inside of this element to add a swipe message. The "startX" and "startTime" variables are set to zero because the "touchmove" or "mousemove" event looks to see if they are set with the "startX" position and start time. If zero, it ignores the move.<br /><br />In the next part we figure out of the device has touch support and set up the event names appropriately:<br /><br /><pre class="prettyprint lang-js">touch = "ontouchend" in document,<br />startEvent = (touch) ? 'touchstart' : 'mousedown',<br />moveEvent = (touch) ? 'touchmove' : 'mousemove',<br />endEvent = (touch) ? 'touchend' : 'mouseup';<br /></pre><br />The "touch" varible is true if the "ontouchend" event exists, which means touch is supported. All that line is doing is looking to see if that object exists.<br /><br />The next variables, "startEvent", "moveEvent" and "endEvent" contain the necessary event names for start, move and end, respectively. Which the next lines of code will now bind to: <br /><pre class="prettyprint lang-js">target<br /> .bind(startEvent, function(e){<br /> // prevent image drag (Firefox)<br /> e.preventDefault();<br /> startTime = e.timeStamp;<br /> startX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX;<br /> })</pre>The "touchstart" or "mousedown" event code contains the following:<br /><br />"e.preventDefault()" prevents the image drag function in Firefox which actually makes you think you're dragging the image instead of swiping. Now we define "startTime" and "startX" as the start time and position of the swipe. The "startX" needs to get the position of the touch from the touches variable, but only the first one. We don't want to swipe if there are multiple touches (gestures) but it shouldn't work anyway since we're not binding to the gestures event. <br /><pre class="prettyprint lang-js">.bind(endEvent, function(e){<br /> startTime = 0;<br /> startX = 0;<br />})</pre>Now the "touchend" or "mouseup" event code only clears the start time and position variables. The reason for this, which was a fun and painful lesson, is that these events don't fire when your mouse/finger leaves the element or goes off the screen. So the actual swipe calculations are done in the move event functions. <br /><br /><pre class="prettyprint lang-js">.bind(moveEvent, function(e){<br /> e.preventDefault();<br /> var currentX = e.originalEvent.touches ? e.originalEvent.touches[0].pageX : e.pageX,<br /> currentDistance = (startX === 0) ? 0 : Math.abs(currentX - startX),<br /> // allow if movement &lt; 1 sec<br /> currentTime = e.timeStamp;<br /> if (startTime !== 0 &amp;&amp; currentTime - startTime &lt; maxTime &amp;&amp; currentDistance &gt; maxDistance) {<br /> if (currentX &lt; startX) {<br /> // swipe left code here<br /> target.find('h1').html('Swipe Left').fadeIn();<br /> setTimeout(function(){<br /> target.find('h1').fadeOut();<br /> }, 1000);<br /> }<br /> if (currentX &gt; startX) {<br /> // swipe right code here<br /> target.find('h1').html('Swipe Right').fadeIn();<br /> setTimeout(function(){<br /> target.find('h1').fadeOut();<br /> }, 1000);<br /> }<br /> startTime = 0;<br /> startX = 0;<br /> }<br />});</pre><br />The "touchmove" or "mousemove" events is where the main portion of this swipe code exists. Again, we want to prevent the default functionality of the move/drag. The "currentX" variable is set with the current x position of the finger/mouse. Again, the x position is obtained from the touches variable, if available. The difference "currentDistance" between the starting x and current x position is then determined. We need the absolute value of this number (ignore the negative sign if it is there) to see if it is more than the "maxDistance" variable. Also here is another trick to make sure the move event is occurring after a start event, check to make sure the starting x position isn't zero. If it is, set the "currentDistance" variable to zero. We also need determine the "currentTime" current time of the move event.<br /><br />Now, we check to make sure the move event occurred after a start event. This can happen is the start event occurs outside of the element or window. So, first we make sure the "startTime" isn't zero, then we check that the time difference from start to current time is less than one second. And finally, we check that the current distance is more than the max distance setting. If it is, then we have a swipe!<br /><br />YAY finally a swipe! Now to just figure out if it was going to the right or left. We do this by comparing the current position "currentX" to the starting position "startX"; if the current position is less than the start, then it's a swipe to the left and if it is greater, then the swipe was to the right.<br /><br />Last but not least, we set the start time and position back to zero to prevent multiple firing of the swipe event.<br /><br />I hope everything had a clear enough explanation. Please feel free to leave a comment or email me (gmail with wowmotty as the user) with questions or if you need further clarification.Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com11tag:blogger.com,1999:blog-3474748080814775582.post-7972537992883916402011-09-22T08:51:00.002-05:002011-11-14T04:52:35.392-06:00Print a ParagraphBy default, browsers will print the content of an entire page/web page or selected text. With some simple scripting you can make it easier for your users to get the browser to print the contents of a particular block or paragraph. The trick is to copy these contents into a new popup window, then print the contents of that window. I put together a very simple demo, which I should turn into a plugin, of how to do this:<br /><br /><iframe src="http://jsfiddle.net/Mottie/byrDG/embedded/result,js,html,css" style="height: 300px; width: 100%;"></iframe>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com1tag:blogger.com,1999:blog-3474748080814775582.post-78259454264861502122011-09-11T18:33:00.000-05:002012-09-03T01:53:03.761-05:00AnythingSlider ThemesI put together <a href="http://css-tricks.github.com/AnythingSlider-Themes/">ten more themes for AnythingSlider</a>! YAY!<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-_FU7RBBSOfk/Tm1FMHbIIPI/AAAAAAAAADc/h3w8pOeXTsI/s1600/themes.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-_FU7RBBSOfk/Tm1FMHbIIPI/AAAAAAAAADc/h3w8pOeXTsI/s1600/themes.jpg" /></a></div><br /><ul><li>All theme files are meant to be used independently from the plugin's "anythingslider.css" file. So you only need to include one of these theme files.</li><li>There is an additional stylesheet named "wrappers.css" which only targets the wrapper around the slider. It is independent of the theme files and meant to be used by extracting out any specific wrapper style you would like to use.</li><li>The first default theme is meant to be used as the base to make your own theme, but without using images or css3.</li><li>The second default theme is also meant to be used as a base for your own theme, but includes images (no css3 though)</li><li>The rest of the themes are free to use.</li></ul>If you would like to contribute a theme, either <a href="https://github.com/CSS-Tricks/AnythingSlider-Themes">fork the repository</a>, add your theme then send me a pull request or just email me the files - my gmail account, user name is wowmotty.<br /><br />Thanks and enjoy!Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-61994794212274429632011-08-17T17:26:00.002-05:002012-09-03T01:48:58.789-05:00AnythingSlider FX BuilderSo, to make it easier to figure out how to add FX to AnythingSlider, I put together a bookmarklet (<a href="https://github.com/CSS-Tricks/AnythingSlider-Fx-Builder">get it from here</a>) that allows you to build/play with the effects live :)<br /><br /><img src="http://i53.tinypic.com/29essnq.jpg" width="600" /><br /><br />If you need instructions, I have some very basic information in the <a href="https://github.com/CSS-Tricks/AnythingSlider-Fx-Builder">readme file</a> on github.<br /><br />Right now, it's still in beta and it only works on the first slider on the page. I'd appreciate any feedback on what could be improved, changed, or if you find any problems.<br /><br />Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-28294573249231170512011-07-12T19:59:00.000-05:002014-01-30T10:43:29.726-06:00Adding WordPress Quicktag Buttons to a WP PluginDisclaimer: I am a total noob when it comes to WordPress and WordPress plugins.<br /><br />I was wondering how difficult it would be to add a button to the WordPress Post Editor. I've seen a few plugins do it, but I discovered that they had to add their own version of TinyMCE to do it.<br /><br />So I searched around on the internet, and found a few methods. But they either <a href="http://granades.com/2007/02/14/adding-quicktags-to-wordpress/">stopped working</a> or required you to <a href="http://wpcandy.com/teaches/tutorial-adding-buttons-to-the-post-editor">edit your quicktags.js file</a> to work, then again after each Wordpress update. So I looked into it and I found a solution that works with the current version 3.2 and hopefully future versions without requiring you to edit or replace the quicktags file.<br /><br /><div class="separator" style="clear: both;">Before</div><div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-NoGIy_lHP-E/ThzoaH2GGWI/AAAAAAAAADU/Lsp82SSBir0/s1600/new-post-before.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="190" src="http://1.bp.blogspot.com/-NoGIy_lHP-E/ThzoaH2GGWI/AAAAAAAAADU/Lsp82SSBir0/s400/new-post-before.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both;">After</div><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-qKzHHJHomN8/ThzodApkLuI/AAAAAAAAADY/RM5oRwsVLvA/s1600/new-post-after.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="207" src="http://4.bp.blogspot.com/-qKzHHJHomN8/ThzodApkLuI/AAAAAAAAADY/RM5oRwsVLvA/s400/new-post-after.jpg" width="400" /></a></div><br /><div class="separator" style="clear: both;">Note: This code is intended to be added inside your WordPress plugin. Like I said, I'm a total noob with WordPress so I'm not sure if it would work outside of a plugin.</div><br />CSS - This CSS should be outside of the php tags<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;style&gt;<br />/* Simulate the input buttons styles */<br />#ed_toolbar button {<br /> display: inline-block;<br /> vertical-align: middle;<br /> margin: 0;<br /> padding: 0;<br /> min-width: 26px;<br /> height: 24px;<br /> -moz-border-radius: 3px;<br /> -webkit-border-radius: 3px;<br /> border-radius: 3px;<br /> border: #c3c3c3 1px solid;<br /> background: url() repeat-x;<br />}<br />#ed_toolbar button:hover {<br /> border: #aaa 1px solid;<br /> background: #ddd;<br />}<br />/* toolbar button images in span */<br />#ed_toolbar button span {<br /> display: inline-block;<br /> background: transparent no-repeat center center;<br /> padding: 4px 2px;<br /> width: 16px;<br /> height: 16px;<br />}<br /><br />/* toolbar custom button image */<br />#_custom span {<br /> background-image: url(/wp-content/plugins/my-plugin-directory/my-plugin-icon.png);<br />}<br />&lt;/style&gt;</pre></blockquote><br />PHP - Add this to your plugin php<br /><blockquote class="pre"><pre class="prettyprint lang-php">&lt;?php<br />add_action('admin_footer', 'my_plugin_add_button');<br /><br />function my_plugin_add_button(){<br />echo &lt;&lt;&lt;EOT<br />&lt;script&gt;/* &lt;![CDATA[ */<br />var j, n,<br /> last = edButtons.length,<br /> tbar = '';<br /><br />// add a new editor button as follows:<br />// edButtons[edButtons.length] = new edButton('BUTTON ID', 'BUTTON TEXT', 'OPENING TAG', 'CLOSING TAG', 'ACCESSKEY');<br />// Example: edButtons[edButtons.length] = new edButton('_h1', 'H1', '&lt;h1&gt;', '&lt;/h1&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_h1', 'h1', '&lt;h1&gt;', '&lt;/h1&gt;', "h");<br />edButtons[edButtons.length] = new edButton('_h2', 'h2', '&lt;h2&gt;', '&lt;/h2&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_h3', 'h3', '&lt;h3&gt;', '&lt;/h3&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_h4', 'h4', '&lt;h4&gt;', '&lt;/h4&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_h5', 'h5', '&lt;h5&gt;', '&lt;/h5&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_h6', 'h6', '&lt;h6&gt;', '&lt;/h6&gt;', -1);<br />edButtons[edButtons.length] = new edButton('_custom', '&lt;button&gt;', '[myplugin_short_code]', '', -1);<br /><br />// Don't change anything below<br />// ***************************<br />for ( j = last; j &lt; edButtons.length; j++) {<br /> n = edButtons[j];<br /> if (/&lt;button&gt;/g.test(n.display)) {<br /> // matched opening tag =&gt; add a button<br /> tbar += '&lt;button id="' + n.id + '" type="button" class="ed_button" onclick="edInsertTag(edCanvas, ' + j + ');"&gt;&lt;span&gt;&lt;/span&gt;&lt;/button&gt;';<br /> } else {<br /> // add an input<br /> tbar += '&lt;input type="button" id="' + n.id + '" accesskey="' + n.access + '" class="ed_button" onclick="edInsertTag(edCanvas,' + j + ');" value="' + n.display.replace(/\"/g,'\"') + '" /&gt;';<br /> }<br />}<br />document.getElementById('ed_toolbar').innerHTML += tbar;<br />/* ]]&gt; */&lt;/script&gt;<br />EOT;<br />}<br />?&gt;</pre></blockquote>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com4tag:blogger.com,1999:blog-3474748080814775582.post-59027170198446270822011-06-18T11:24:00.009-05:002016-02-18T11:09:31.335-06:00jQuery Tablesorter, The Missing Docs<a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="top"></a> <p>If you need support for my fork of tablesorter, please contact me here: <a class="badge" href="https://gitter.im/Mottie/tablesorter?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"><img src="https://badges.gitter.im/Join%20Chat.svg"></a></p> The Missing Docs for <a href="http://tablesorter.com/docs/index.html">jQuery Tablesorter 2.0</a><br /> <ul><li><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#defaults">Defaults</a></li><li><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#methods">Methods</a></li><li><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#events">Events</a></li></ul><hr /><div><div style="text-align: center;"><span style="font-size: large;"><b>These docs have been incorporated into my</b></span></div><div style="text-align: center;"><span style="font-size: large;"><b><a href="http://mottie.github.com/tablesorter/docs/">Github fork of Tablesorter</a>.</b></span></div></div><hr style="text-align: center;" /><h4>All Defaults</h4><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="defaults"></a><br /><table border="1" cellspacing="0" class="keytable"><tbody><tr align="left"><th colspan="2">Appearance</th></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="cssHeader"></a>cssHeader: "header"</td><td>The CSS style used to style the header in its unsorted state.</td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="cssAsc"></a>cssAsc: "headerSortUp"</td><td>The CSS style used to style the header when sorting ascending.</td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="cssDesc"></a>cssDesc: "headerSortDown"</td><td>The CSS style used to style the header when sorting descending.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#cssChildRow">cssChildRow</a>: "expand-child"</td><td>This allows you to add a child row that is always attached to the parent. See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#cssChildRow">children rows</a> section below.</td></tr><tr><td style="white-space: nowrap;"><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#widgetZebra">widgetZebra</a>: { css: ["even", "odd"] }</td><td>Set the CSS classes used for zebra striping the table. See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#widgetZebra">updating zebra striping</a> section below.</td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="widthFixed"></a>widthFixed: false</td><td>Indicates if tablesorter should apply fixed widths to the table columns. This is useful for the Pager companion. Requires the jQuery dimension plugin to work. See the <a href="http://tablesorter.com/docs/example-pager.html">main demo page</a></td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#onRenderHeader">onRenderHeader</a>: null</td><td>This function is called when classes are added to the th tags. You can use this to append HTML to each header tag. See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#onRenderHeader">Render Header</a> section below.</td></tr><tr align="left"><th colspan="2">Sort Options</th></tr><tr><td>sortInitialOrder: "asc"</td><td>A string of the inital sorting order can be "asc" or "desc". This is overridden bt the <code>sortList</code> settings.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#sortForce">sortForce</a>: null</td><td>Use to add an additional forced sort that is <b>prepended</b> to <code>sortList</code>. For example, <code>sortForce: [[0,0]]</code> will sort the first column in ascending order. See the <a href="http://tablesorter.com/docs/example-option-sort-force.html">main demo page</a></td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#sortList">sortList</a>: []</td><td>Add an array of the order the table should be initially sorted; e.g. <code>sortList: [[0,1], [1,0]]</code>. The first part "[0,1]" will sort the first column (zero based index) in decending order and the second part "[1,0]" is to sort the second column in ascending order. See the <a href="http://tablesorter.com/docs/example-option-sort-list.html">main demo page</a>.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#sortAppend">sortAppend</a>: null</td><td>Use to add an additional default sorting rule, that is <b>appended</b> to the <code>sortList</code>.</td></tr><tr align="left"><th colspan="2">Parsers &amp; Widgets</th></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#headers">headers</a>: {}</td><td>See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#parsers">Change how you sort a column</a> section below or see the <a href="http://tablesorter.com/docs/example-options-headers.html">main demo page</a></td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#textExtraction">textExtraction</a>: "simple"</td><td>See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#textExtraction">Text Extraction</a> section below or see the <a href="http://tablesorter.com/docs/example-option-sort-key.html">main demo page</a></td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortLocaleCompare"></a>sortLocaleCompare: true</td><td>Boolean flag indicating whenever to use String.localeCampare method or not. This is only used when comparing text strings.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#parsers">parsers</a>: {}</td><td>Internal list of all of the parsers. See a <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#parsers">complete list of parsers</a> below.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#widgets">widgets</a>: []</td><td>Initialize widgets using this option (e.g. <code>widgets : ['zebra']</code>, or custom widgets <code>widgets: ['zebra', 'myCustomWidget']</code>; see this demo on <a href="http://tablesorter.com/docs/example-widgets.html">how to add a custom widget</a>.)</td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="selectorHeaders"></a>selectorHeaders: 'thead th'</td><td>jQuery selectors used to find the header cells. You can change this, but the table will still need the required thead and tbody before this plugin will work properly.</td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="headerList"></a>headerList: []</td><td>Internal list of each header element as selected using jQuery selectors in the <code>selectorHeaders</code> option.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#dateFormat">dateFormat</a>: "us"</td><td>Set the date format. See the <a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#dateFormat">dataFormat</a> section below.</td></tr><tr align="left"><th colspan="2">Interaction</th></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortMultiSortKey"></a>sortMultiSortKey: "shiftKey"</td><td>The key used to select more than one column for multi-column sorting. Defaults to the shift key. Other options might be ctrlKey, altKey. See the <a href="http://tablesorter.com/docs/example-option-sort-key.html">main demo page</a></td></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="cancelSelection"></a>cancelSelection: true</td><td>Indicates if tablesorter should disable selection of text in the table header (TH). Makes header behave more like a button.</td></tr><tr align="left"><th colspan="2">Unknown</th></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="decimal"></a>decimal: '/\.|\,/g'</td><td>Regex to find the decimal "." for U.S. format and "," for European format... but I can't find it being used anywhere in the plugin.</td></tr><tr align="left"><th colspan="2">Debugging</th></tr><tr><td><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="debug"></a>debug: false</td><td>Boolean flag indicating if tablesorter should display debuging information usefull for development. This displays information in the console, if available, or in alerts. See the <a href="http://tablesorter.com/docs/example-option-debug.html">main demo page</a></td></tr></tbody></table><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="update-default"></a><br /><h4>Update Default Settings</h4>Update any of the default settings above using this format:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(selector).data("tablesorter").{default name} = newValue;</pre></blockquote>Here are a few examples:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">// Change sortForce<br />$("table").data("tablesorter").sortForce = [[1,0]];<br />// it appears that the only way to update the table is to resort it<br />$("table").trigger("sorton", [[0,1]]);<br />// Add zebra sorting after the table has initialized<br />$("table").data("tablesorter").widgets = ["zebra"];<br />// there is a method to apply widgets!<br />$("table").trigger("applyWidgets");</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="cssChildRow"></a><br /><h4>Child Rows</h4>To add a row that remains attached to a parent row, add the "expand-child" class to the row.<br /><br />You can add some scripting to hide these child rows and have them expand when you click on a link; this is the original reason for the css name.<br /><br />Here is the <a href="http://www.pengoworks.com/workshop/jquery/tablesorter/tablesorter.htm">original mod page</a>, the code was added into the TableSorter core but remains undocumented.<br /><br />This makes the tablesorter plugin keep these rows together. This css class can be changed using the "cssChildRow" option.<br /><br />Here is some example markup:<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;table width="100%" border="1"&gt;<br /> &lt;thead&gt;<br /> &lt;tr&gt;<br /> &lt;th&gt;Item #&lt;/th&gt;<br /> &lt;th&gt;Name&lt;/th&gt;<br /> &lt;th&gt;Available&lt;/th&gt;<br /> &lt;/tr&gt;<br /> &lt;/thead&gt;<br /> &lt;tbody&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;12345&lt;/td&gt;<br /> &lt;td&gt;Toy Car&lt;/td&gt;<br /> &lt;td&gt;5&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr class="expand-child"&gt; &lt;!-- this row will remain attached to the above row, and not sort separately --&gt;<br /> &lt;td colspan="3"&gt;<br /> It's a toy car!<br /> &lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;23456&lt;/td&gt;<br /> &lt;td&gt;Toy Plane&lt;/td&gt;<br /> &lt;td&gt;2&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr class="expand-child"&gt; &lt;!-- this row will remain attached to the above row, and not sort separately --&gt;<br /> &lt;td colspan="3"&gt;<br /> It's a toy plane!<br /> &lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr class="expand-child"&gt; &lt;!-- this row will remain attached to the above two rows, and not sort separately --&gt;<br /> &lt;td colspan="3"&gt;<br /> and it flies!<br /> &lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;/tbody&gt;<br />&lt;/table&gt;</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="widgets"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="widgetZebra"></a><br /><h4>Update Zebra Striping After Sorting</h4>Actually the zebra striping widget does this for you automatically.<br /><br />By default the odd and even rows are styled using "odd" and "even" class names (<code>{ css: [ "even", "odd" ] }</code>).<br /><br />Change these css class names using the <code>widgetZebra</code> option as follows:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> widgets: ["zebra"], // initialize zebra striping of the table<br /> widgetZebra: { css: [ "normal-row", "alt-row" ] }<br /> });</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="onRenderHeader"></a><br /><h4>Modify the Header Markup</h4>The <code>onRenderHeader</code> option allows you to add a function that which can modify the HTML of each header tag. In the example below, the header cell (<code>th</code>) content is wrapped with a span tag to allow for better styling (<a href="http://www.pengoworks.com/workshop/jquery/tablesorter/tablesorter.htm">source</a>).<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> onRenderHeader: function (){<br /> this.wrapInner('&lt;span class="roundedCorners"&gt;&lt;/span&gt;');<br /> }<br /> });<br />});</pre></blockquote>and you'll end up with this (only the thead is shown):<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;thead&gt;<br /> &lt;tr&gt;<br /> &lt;th class="header"&gt;&lt;span class="roundedCorners"&gt;Column 1&lt;/span&gt;&lt;/th&gt;<br /> &lt;th class="header"&gt;&lt;span class="roundedCorners"&gt;Column 2&lt;/span&gt;&lt;/th&gt;<br /> &lt;/tr&gt;<br />&lt;/thead&gt;</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="parsers"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="headers"></a><br /><h4>Change how you sort a column</h4>The plugin attempts to detect the type of data that is contained in a column, but if it can't figure it out then it defaults to alphabetical.<br /><br />You can easily override this by setting the header argument (or column parser).<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> headers: {<br /> 0: { sorter: false }, // disable first column<br /> 1: { sorter: "digit" }, // sort second column numerically<br /> 4: { sorter: "shortDate" } // sort the fifth column by date (e.g. mm/dd/yyyy if the date format is "us")<br /> }<br /> });<br />});</pre></blockquote>*Note* the header number starts with zero (zero based index).<br /><br />Here is a list of available sorter values/parsers:<br /><br /><table border="1" cellspacing="0" class="keytable"><tbody><tr><td>sorter: false</td><td>disable sort for this column.</td></tr><tr><td>sorter: "text"</td><td>Sort alphabetically.</td></tr><tr><td>sorter: "digit"</td><td>Sort numerically.</td></tr><tr><td>sorter: "currency"</td><td>Sort by currency value (supports "£$€").</td></tr><tr><td>sorter: "ipAddress"</td><td>Sort by IP Address.</td></tr><tr><td>sorter: "url"</td><td>Sort by url.</td></tr><tr><td>sorter: "isoDate"</td><td>Sort by ISO date (YYYY-MM-DD or YYYY/MM/DD).</td></tr><tr><td>sorter: "percent"</td><td>Sort by percent.</td></tr><tr><td>sorter: "usLongDate"</td><td>Sort by date (U.S. Standard, e.g. Jan 18, 2001 9:12 AM).</td></tr><tr><td>sorter: "shortDate"</td><td>Sort by a shorten date (see "dateFormat").</td></tr><tr><td>sorter: "time"</td><td>Sort by time (23:59 or 12:59 pm).</td></tr><tr><td>sorter: "metadata"</td><td>Sort by the sorter value in the metadata - requires the metadata plugin.</td></tr></tbody></table><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="dateFormat"></a><br /><h4>Change the Date Format (dateFormat)</h4><table border="1" cellspacing="0" class="keytable"><tbody><tr><td>"us"</td><td>"mm-dd-yyyy" or "mm/dd/yyyy"</td></tr><tr><td>"uk"</td><td>"dd-mm-yyyy" or "dd/mm/yyyy"</td></tr><tr><td>"dd/mm/yy" or<br />"dd-mm-yy"</td><td>Sort by short year (it appears to sort by day first, not the year)</td></tr></tbody></table><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="resort"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortForce"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortList"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortAppend"></a><br /><h4>Change the Sorted Columns</h4>There are three options to determine the sort order and this is the order of priority:<br /><ol><li><code>sortForce</code> forces the user to have this/these column(s) sorted first (null by default).</li><li><code>SortList</code> is the initial sort order of the columns.</li><li><code>SortAppend</code> is the default sort that is added to the end of the users sort selection (null by default).</li></ol>The value of these sort options is an array of arrays and can include one or more columns. The format is an array of instructions for per-column sorting and direction in the format: <code>[[columnIndex, sortDirection], ... ]</code> where <code>columnIndex</code> is a zero-based index for your columns left-to-right and <code>sortDirection</code> is 0 for Ascending and 1 for Descending. A valid argument that sorts ascending first by column 1 and then column 2 looks like: [[0,0],[1,0]]<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> sortForce : [[0,0]], // always sort first column first<br /> sortList : [[1,0], [2,0]], // initial sort columns (2nd and 3rd)<br /> sortAppend : [[3,0]] // always add this sort on the end (4th column)<br /> });<br />});</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="textExtraction"></a><span style="color: red;"><b>*NOTE*</b></span>&nbsp;When writing this post, I didn't realize that the sortAppend option was listed as an option, but the code for it did not exist. This problem is fixed in my fork of tablesorter (<a href="http://mottie.github.io/tablesorter/docs/example-option-sort-append.html">example</a>).<br /><br /><h4>Text Extraction</h4>This is demonstrated in the <a href="http://tablesorter.com/docs/example-option-text-extraction.html">Dealing with markup inside cells</a> demo page, but the example uses vanilla javascript to find the desired text, like this:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> textExtraction: function(node) {<br /> return node.childNodes[0].childNodes[0].innerHTML;<br /> }<br /> });<br />});</pre></blockquote>But you could make your life easier and just make the node into a jQuery object and then "find" what you need:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> textExtraction: function(node) {<br /> return $(node).find("span:last").text();<br /> }<br /> });<br />});</pre></blockquote>Now if the text you are finding in the script above is say a number, then just include the headers sorter option to specify how to sort it. Also in this example, we will specify that the special textExtraction code is only needed for the second column ("1" because we are using a zero-based index). All other columns will ignore this textExtraction function. <br /><span style="color: red;"><b><br /></b></span><br /><span style="color: red;"><b>*NOTE*</b></span>&nbsp;When writing this post, I didn't realize that the textExtraction option did not allow defining it per column, as shown below, but this has been fixed in the forked github version - <a href="http://mottie.github.com/tablesorter/docs/example-option-text-extraction.html">example</a>.<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table").tablesorter({<br /> textExtraction: {<br /> 1: function(node) {<br /> return $(node).find("span:last").text();<br /> }<br /> },<br /> headers: {<br /> 1: { sorter : "digit" }<br /> }<br /> });<br />});</pre></blockquote><hr /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="methods"></a><br /><h4>Methods</h4>See examples of each further down<br /><table border="1" cellspacing="0" class="keytable"><tbody><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#sorton">sorton</a></td><td>Resort the table using new sort parameters.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#update">update</a></td><td>Update the stored tablesorter data and the table.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#appendCache">appendCache</a></td><td>Update a table that has had its data dynamically updated; used in conjunction with "update"</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#updateCell">updateCell</a></td><td>Update a table cell in the tablesorter data.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#applyWidgetId">applyWidgetId</a></td><td>Apply a widget (wrapped in square brackets) to the table one time only.</td></tr><tr><td><a href="http://wowmotty.blogspot.com/2011/06/jquery-tablesorter-missing-docs.html#applyWidgets">applyWidgets</a></td><td>Apply previously selected widgets to the table - will update the widget with new sort.</td></tr></tbody></table><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sorton"></a><br /><h4>Resort the table - "sorton" method</h4>See the full example in the tablesorter docs: <a href="http://tablesorter.com/docs/example-trigger-sort.html">Sort table using a link outside the table</a><br /><blockquote class="pre"><pre class="prettyprint lang-js;">// Choose a new sort order<br />var sort = [[0,0],[2,0]];<br />// Note that the sort value below is inside of another array (inside another set of square brackets)<br />$("table").trigger("sorton", [sort]);</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="update"></a><br /><h4>Update the Table</h4>See the full example in the tablesorter docs: <a href="http://tablesorter.com/docs/example-ajax.html">Appending table data with ajax</a><br /><blockquote class="pre"><pre class="prettyprint lang-js;">// Add new content<br />$("table tbody").append(html);<br />// let the plugin know that we made a update<br />$("table").trigger("update");<br />// set sorting column and direction, this will sort on the first and third column<br />var sorting = [[2,1],[0,0]];<br />// sort on the first column<br />$("table").trigger("sorton", [sorting]);</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="appendCache"></a><br /><h4>Append Cache</h4>If you dynamically change the table content, more than just one cell like in the "updateCell" example above, you may possibly have to trigger two events: "update" and "appendCache".<br /><br />This answer is from a <a href="http://stackoverflow.com/questions/247305/using-jquery-tablesorter-on-dynamically-modified-table">StackOverflow answer</a><br /><blockquote class="pre"><pre class="prettyprint lang-js;">// Table data was just dynamically updated<br />$(table)<br /> .trigger("update")<br /> .trigger("appendCache");</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="updateCell"></a><br /><h4>Update a Table Cell</h4>Example from these alternate tablesorter docs: <a href="http://www.matalasco.com/childPages/docs/example-update-cell.html">Updating the table cache</a> - the demo doesn't work, but I've tested the example code below and it works properly.<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function() {<br /> $("table").tablesorter();<br /> $("td.discount").click(function(){<br /> // randomize a number<br /> var discount = '$' + Math.round(Math.random() * Math.random() * 100) + '.' + ('0' + Math.round(Math.random() * Math.random() * 100)).slice(-2);<br /> $(this).text(discount);<br /> // update the table, so the tablesorter plugin knows its value<br /> $("table").trigger("updateCell",[this]);<br /> // set sorting column and direction, this will sort on the first and third column<br /> var sorting = [[3,1]];<br /> // sort on the first column<br /> $("table").trigger("sorton",[sorting]);<br /> return false;<br /> });<br />});</pre></blockquote><blockquote class="pre"><pre class="prettyprint lang-html">&lt;table class="tablesorter" cellspacing="1"&gt;<br /> &lt;thead&gt;&gt;<br /> &lt;tr&gt;<br /> &lt;th&gt;first name&lt;/th&gt;<br /> &lt;th&gt;last name&lt;/th&gt;<br /> &lt;th&gt;age&lt;/th&gt;<br /> &lt;th&gt;total&lt;/th&gt;<br /> &lt;th&gt;discount&lt;/th&gt;<br /> &lt;th&gt;date&lt;/th&gt;<br /> &lt;/tr&gt;<br /> &lt;/thead&gt;<br /> &lt;tbody&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;peter&lt;/td&gt;<br /> &lt;td&gt;parker&lt;/td&gt;<br /> &lt;td&gt;28&lt;/td&gt;<br /> &lt;td class="discount"&gt;$9.99&lt;/td&gt;<br /> &lt;td&gt;20%&lt;/td&gt;<br /> &lt;td&gt;jul 6, 2006 8:14 am&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;john&lt;/td&gt;<br /> &lt;td&gt;hood&lt;/td&gt;<br /> &lt;td&gt;33&lt;/td&gt;<br /> &lt;td class="discount"&gt;$19.99&lt;/td&gt;<br /> &lt;td&gt;25%&lt;/td&gt;<br /> &lt;td&gt;dec 10, 2002 5:14 am&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;clark&lt;/td&gt;<br /> &lt;td&gt;kent&lt;/td&gt;<br /> &lt;td&gt;18&lt;/td&gt;<br /> &lt;td class="discount"&gt;$15.89&lt;/td&gt;<br /> &lt;td&gt;44%&lt;/td&gt;<br /> &lt;td&gt;jan 12, 2003 11:14 am&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;bruce&lt;/td&gt;<br /> &lt;td&gt;almighty&lt;/td&gt;<br /> &lt;td&gt;45&lt;/td&gt;<br /> &lt;td class="discount"&gt;$153.19&lt;/td&gt;<br /> &lt;td&gt;44%&lt;/td&gt;<br /> &lt;td&gt;jan 18, 2001 9:12 am&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;tr&gt;<br /> &lt;td&gt;bruce&lt;/td&gt;<br /> &lt;td&gt;evans&lt;/td&gt;<br /> &lt;td&gt;22&lt;/td&gt;<br /> &lt;td class="discount"&gt;$13.19&lt;/td&gt;<br /> &lt;td&gt;11%&lt;/td&gt;<br /> &lt;td&gt;jan 18, 2007 9:12 am&lt;/td&gt;<br /> &lt;/tr&gt;<br /> &lt;/tbody&gt;<br />&lt;/table&gt;</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="applyWidgetId"></a><br /><h4>Apply a New Widget "applyWidgetId"</h4>Apply a widget to the table. In this example, we apply the zebra striping to the table after it has been initialized.<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> // initialize tablesorter without the widget<br /> $("table").tablesorter();<br /> <br /> // click a button to apply the zebra striping<br /> $("button").click(function(){<br /> $('table').trigger('applyWidgetId', ['zebra']);<br />});</pre></blockquote><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="applyWidgets"></a><br /><h4>Apply Chosen Widgets "applyWidgets"</h4>Reapply a widget to the table. In this example, we apply the zebra striping to the table after it has been initialized.<br /><br />This is basically an alternate method to the "applyWidgetId", but in the example below you can add or remove widgets from the chosen list.<br /><blockquote class="pre"><pre class="prettyprint lang-js;">// Update the list of widgets to apply to the table (add or remove)<br />$("table").data("tablesorter").widgets = ["zebra"];<br />$("table").trigger('applyWidgets');</pre></blockquote><hr /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="events"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortStart"></a><br /><a href="http://www.blogger.com/post-edit.g?blogID=3474748080814775582&amp;postID=5902717019844627082" id="sortEnd"></a><br /><h4>Triggered Events</h4><table border="1" cellspacing="0" class="keytable"><tbody><tr><td>sortStart</td><td>This event fires when the tablesorter plugin is about to start resorting the table.</td></tr><tr><td>sortEnd</td><td>This event fires when the tablesorter plugin has completed resorting the table.</td></tr></tbody></table><br /><br />Bind to the "sortStart" and/or the "sortEnd" events as shown on the <a href="http://tablesorter.com/docs/example-triggers.html">example page</a><br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> // initialize the tablesorter plugin<br /> $("table").tablesorter();<br /> // bind to sort events<br /> $("table")<br /> .bind("sortStart",function() {<br /> $("#overlay").show();<br /> })<br /> .bind("sortEnd",function() {<br /> $("#overlay").hide();<br /> });<br />});</pre></blockquote>Or here is another example from <a href="http://stackoverflow.com/questions/5265418/tablesorter-jquery-hack">StackOverflow</a><br /><blockquote class="pre"><pre class="prettyprint lang-js;">$(function(){<br /> $("table")<br /> .tablesorter() <br /> .bind("sortEnd", function(){ <br /> $("table tr").removeClass("block");<br /> // adds the "block" class to every 8th row<br /> $("table tr:nth-child(8)").addClass("block");<br /> });<br />});</pre></blockquote>Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com82tag:blogger.com,1999:blog-3474748080814775582.post-74124319854533515852011-06-06T18:25:00.000-05:002011-06-06T18:25:50.018-05:00EqualizerThis script was first written by <a href="http://stephenakins.blogspot.com/2011/01/uniform-div-heights-for-liquid-css-p.html">Stephen Akins</a> to equalize column heights in multiple rows. Chris Coyier of <a href="http://css-tricks.com/">CSS Tricks</a> then <a href="http://css-tricks.com/equal-height-blocks-in-rows/">posted an update</a> which allowed the heights to adjust while resizing a page. I simply debugged, added a few improvements (like minimum and maximum height, an idea from <a href="https://github.com/madmike1029/matchHeights">Mike Avello's plugin</a>) and turned it into a plugin.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://github.com/Mottie/Equalizer" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="276" src="http://4.bp.blogspot.com/-PNbyaefhFEE/Te1fMRlqPoI/AAAAAAAAADQ/34iucnMs01c/s400/Equalizer.jpg" width="400" /></a></div><br />In the screen shot above, there are three blocks containing three columns. The three blocks have identical HTML. The left block is unmodified. The center block has <a href="https://github.com/Mottie/Equalizer">Equalizer</a> applied with a max height setting; and each block that exceeds the max height has a css class applied which sets the overflow to auto and adds the different background color for the demo, but you can do whatever you want via the css. And the right block has <a href="https://github.com/Mottie/Equalizer">Equalizer</a> applied with a minimum height setting; you should be able to see in the screen shot that the middle row of the right block has a minimum height set.<br /><br />Try out <a href="http://mottie.github.com/Equalizer/">the demo</a>, resize your browser window and see how everything changes.Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-5430974038034976902011-05-18T16:18:00.001-05:002011-05-18T16:19:25.710-05:00The Base TagWhenever someone asks me to help troubleshoot a problem with their site, if I can't immediately see the problem or fix it using Firebug, I download the page as an HTML only file. Then you have to deal with pages that have relative links to code, style sheets or images:<br /><blockquote class="pre"><pre class="brush: js; html-script: true">&lt;link href="/css/main.css" rel="stylesheet"&gt;</pre></blockquote>which you will probably have to use your editor to find and replace each one. A big time saver is using the apparently less known <a href="http://www.w3.org/TR/html401/struct/links.html#edef-BASE">base tag</a>, in which you add the base URL to the site.<br /><blockquote class="pre"><pre class="brush: js; html-script: true">&lt;base href="http://some-site.com"&gt;</pre></blockquote>Then if you need to modify a file for testing, just download it to your computer (e.g. your desktop), open it in your browser, then copy the URL. Replace the file's url with this one, since the relative URL will look to the base tag instead of your desktop:<br /><blockquote class="pre"><pre class="brush: js; html-script: true">&lt;link href="file:///C:/Desktop/main.css"&gt;</pre></blockquote>Now when you open the page in your browser, it should look exactly like the online page.&nbsp; :)Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com0tag:blogger.com,1999:blog-3474748080814775582.post-26417819776230655982011-05-15T22:19:00.002-05:002014-01-30T10:48:05.812-06:00jQuery find/replace text without destroying DOM elementsI found this question on <a href="http://stackoverflow.com/questions/6012163/whats-a-good-alternative-to-html-rewriting">StackOverflow</a> on how to replace text without destroying event handlers, DOM nodes and such. He just wanted to replace a name inside a block of HTML. The name could be inside of an LI, A (link), p or div element, etc. Here is the example HTML markup provided:<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;div id="test"&gt;<br /> &lt;h1&gt;An article about John&lt;/h1&gt;<br /> &lt;p&gt;The first paragraph is about John.&lt;/p&gt;<br /> &lt;p&gt;The second paragraph contains a &lt;a href="#"&gt;link to John's CV&lt;/a&gt;.&lt;/p&gt;<br /> &lt;div class="comments"&gt;<br /> &lt;h2&gt;Comments to John's article&lt;/h2&gt;<br /> &lt;ul&gt;<br /> &lt;li&gt;Some user asks John a question.&lt;/li&gt;<br /> &lt;li&gt;John responds.&lt;/li&gt;<br /> &lt;/ul&gt;<br /> &lt;/div&gt;<br />&lt;/div&gt;<br /></pre></blockquote>You couldn't simply grab the html() and replace the text because it the result replaces all of the elements inside of "#test" and thus breaks all previously attached functions (click, hover, etc.) or any entered form data inside of inputs (<a href="http://jsfiddle.net/Mottie/ma8Td/">demo</a>):<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$('#test').html(function(i, v) {<br /> return v.replace(/John/g, 'Peter'); <br />});</pre></blockquote>Instead you'd have to step through each text node, find the name, then replace it - it's quite a chunk of code. But I think I found a <i>quick and dirty</i> solution (<a href="http://jsfiddle.net/Mottie/ma8Td/1/">demo</a>) that works on the HTML markup above:<br /><blockquote class="pre"><pre class="prettyprint lang-js;">$('#test :not(:has(*))').text(function(i, v) {<br /> return v.replace(/John/g, 'Peter'); <br />});</pre></blockquote>What this does is find all elements that don't have children, and replace the text inside.<br /><br />To understand what the selector is doing, first imagine the selector like this: "#test *:not(:has(*))". So reading it out in plain language would sound like this: Find the element with an ID of test, then find all elements within it (the first asterisk "*"), then find the opposite ":not()" of all elements that have children ":has(*)", meaning elements that don't have children. Then get the text and replace it.<br /><br />But this quick and dirty method does have an <b>important limitation</b> which is that it would ignore text inside an element that has child elements. Here is an example of what I mean where the link is treated as a child element and thus the name is not replaced (nothing would change):<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;div id="test"&gt;<br /> &lt;p&gt;Visit John's &lt;a href="blog.html"&gt;blog&lt;/a&gt;&lt;/p&gt;<br />&lt;/div&gt;<br /></pre></blockquote>So, if you do have markup like as it is above, then just wrap the name in a span (<a href="http://jsfiddle.net/Mottie/ma8Td/2/">demo</a>):<br /><blockquote class="pre"><pre class="prettyprint lang-html">&lt;div id="test"&gt;<br /> &lt;p&gt;Visit &lt;span&gt;John's&lt;/span&gt; &lt;a href="blog.html"&gt;blog&lt;/a&gt;&lt;/p&gt;<br />&lt;/div&gt;<br /></pre></blockquote>If you don't or can't wrap the name, then you can't use the quick and dirty method I posted above. You'll have to use this textWalk plugin by PatrickDW (<a href="http://jsfiddle.net/v2yp5/4/">a demo</a>, and see <a href="http://stackoverflow.com/questions/6012163/whats-a-good-alternative-to-html-rewriting/6012345#6012345">his answer</a> for full details) or check out <a href="http://stackoverflow.com/questions/4060056/jquery-find-replace-without-changing-original-text/4060635#4060635">Bobince</a>'s answer in this question for a similar method of walking through the text nodes.Rob Garrisonhttps://plus.google.com/109592049038767340147noreply@blogger.com2