Something I have been asked today:
Keypress, stacking of events, control of values and setTimeout
Choan, my dear, guapetón of my soul… I have two fields on a form and I want that one to reflect the changes in the other… and controlling keypress I don’t have the updated value
Not really, because you don’t have it. Solution: control the execution stack. The trick: use a timeout for the method that updates the values to run after finishing with the management of events. Something like this:
// in the handler of the keypress event
setTimeout(updateValue, 0); // <-- the key is in the zero
Now, to pick up the updated value based on
keypress
it is not enough, because there are a thousand ways to fill in the field without pressing a key (copipegar menus, bar code readers, I-that-sés…)
So my advice is to defend yourself by controlling also the event
blur
. I put it in terms of jQuery, assuming that the field that control takes the id source and the destination is known as a destination.
(function($) {
var init_copy_values = function() {
$('#source').bind('keypress blur', function(e) {
if (e.type == 'keypress') setTimeout(updateValue, 0);
else updateValue();
});
},
updateValue = function() {
$('#destination').val($('#source').val());
};
$(init_copy_values);
}(jQuery));
Well, nothing, already, a post without insulting anyone.
Vilaweb and the developer stupid bean
I counted out there today has released the new version of Vilaweb. The culmination of the best practices in HTML markup (yes, that is sarcasm).
Attention to the following fragment, taken from the website on November 24, 2009:
<div class="zona-imatge">
<img alt="Detingut el batlle de Polop per la mort del seu predecessor<br/>" src="/media/portada/81/1259046000.jpg" width="299" height="254"/>
<div class="params" style="display: none">
<div class="w">299</div>
<div class="h">254</div>
<div class="file">/media/portada/81/1259046000.jpg</div>
</div>
</div>
My comment (brief version): asshole.
My comment (extended version): are you so stupid that every time you are checking, instead of improve create a detriment. What the hell are those “parameters” putting noise into the HTML? How did you happen, perchance, to apply a sort of “non-intrusiveness” born by your great brain?
I guess that you will be thinking: are you okay, Choan, despotricas and don’t say anything. This is, in effect. If you know what is wrong (fatal, punishable with death) in that piece of code you don’t need to count anything. And if you do not know, follow in your holy ignorance, seeks a multinational company in the to succeed as a programmer mediocre, and make your mother feel proud of you. Fool.
(There are other pearls, but the more desire he has given me of myself faltón has been this. And yes, the design also seems to me like a shit.)
NOT-SO-RECENT
Number of days in a month
See a cow on top of a shed is nothing compared to the things that one is found in the scripts that run through these worlds of god.
One of the fields in which any mindundi is able to apply the most of your ignorance of javascript is the calculation of days in a month. Use a dozen conditional is loose.
The ultimate: when you need to take accounts, about anything, using the tools of the language. Do not accounts of the old.
The code. Function that receives a one-month (in numeric notation-human) and a year (optional, if not received we take the current year and returns the number of days in the month. No conditional was damaged during the experiment.
function daysInMonth(humanMonth, year) {
return new Date(year || new Date().getFullYear(), humanMonth, 0).getDate();
}
daysInMonth(2, 2009); // 28
daysInMonth(2, 2008); // 29
Keys:
ECMAScript manages internally dates as numeric data (see Unix Time)
in ECMAScript, the months are numbered from 0 to 11 and days from 1 to N. We feed the function with a month human. So the date is created (d) correspond to day 0 of the month following the month that we seek. The algorithm of creation of dates is tolerant (basically the sum of what we give and get an integer, so the day 0 of a month is always the last day of the previous month).
In the end, that all of this is that the machine is cleverer than you are and your knuckles. And all the bloggers together, including the menda.
ADDENDUM
We can use a stratagem similar to to know if the year is a leap year:
function isLeapYear(year) {
return new Date(year, 0, 366).getFullYear() == year;
}
Working with jQuery: timeline an inflatable, espaguetismo to the plugin
In the chapter today, we will see how to convert a spaghetti-code in a nice plugin reusable.
Target widget: allow to scroll horizontally by dragging (skewer, drag, loose) a block of content. If you are in a hurry to see what just the thing, go straight to the demo.
Elements: a container (with
overflow: auto
) and their content. For our example:
<div id="#timeline">
<ul>
<li>...</li>
<li>...</li>
</ul>
</div>
With a few styles such that:
#timeline {
width: 600px;
height: 400px;
overflow: auto;
}
#timeline ul {
width: 1000px;
margin: 0;
padding: 0;
}
#timeline li {
display: block;
width: 200px;
padding: 10px;
margin: 0;
float: left;
}
On these stones we will build our church. Let’s step through the code necessary to make the thing move. We will put in place the machinery when clicking on the container:
$('#timeline')
.bind('mousedown', function(e) {
// aquí vendrá nuestro código
});
In order to avoid the use of global variables, we use
data
, a method in jQuery that allows us to maintain relationships between values and DOM elements. We will maintain the following data: current position of the scroll element to which it affects and the position of the pointer at the time of the
mousedown
.
$(document)
.data('timeline', {
element: this,
scroll: this.scrollLeft,
x : e.clientX
});
Notice that we are preserving the data in the document. In every moment, there will be a maximum of one element in displacement.
Well, it has begun the action… but still we are not following the movements of the pointer. Assign a couple of event handlers for the events
mousemove
and
mouseup
from
document
.
Why to
document
and not for the container with which we are dealing? Even if the pointer leaves the container during the drag, we want to continue moving the contents. And if you go out and not controláramos
mouseup
at the document level, our script will never abandon him the way we are “dragging stuff”.
As we are cool and modern, we will take advantage of the events.with-space-of-names that jQuery kindly put at our disposal. For
mousemove
, we retrieve the data we stored in
mousedown
and modify the property
scrollLeft
of the container plus the difference between the current mouse position and the stored. In
mouseup
, we clean the data and remove event handlers from the space
timeline
assigned to
document
.
jQuery(document)
.bind('mousemove.timeline', function(e) {
var data = jQuery(this).data('timeline');
data.element.scrollLeft = data.scroll + data.x - e.clientX;
})
.bind('mouseup.timeline', function(e) {
jQuery(this)
.removeData('timeline')
.unbind('.timeline');
});
To finish off, we will cancel the default action of
mousedown
for not selecting text as you drag:
e.preventDefault();
Putting it all together and within a call to
ready
, we have the thingy working:
jQuery(document).ready(function() {
jQuery('#timeline')
.bind('mousedown', function(e) {
jQuery(document)
.data('timeline', {
element: this,
scroll: this.scrollLeft,
x : e.clientX
})
.bind('mousemove.timeline', function(e) {
var data = jQuery(this).data('timeline');
data.element.scrollLeft = data.scroll + data.x - e.clientX;
})
.bind('mouseup.timeline', function(e) {
jQuery(this)
.removeData('timeline')
.unbind('.timeline');
});
e.preventDefault();
});
});
Great! With this we already have the desired behavior, but… does not seem very reusable. Pluguinifiquemos.
BUILDING A PLUGIN
When we invoke the function
jQuery
(or its alias habitual,
$
), we retrieve a object. That object has a prototype. To add methods to the object returned by
jQuery
, add them to your prototype. So something such that:
jQuery.prototype.miMetodo = function() {
};
We would go shooting. But there is a language more jotacueriesco to say the same thing. Of step add a
return this
our method to allow chaining of calls:
jQuery.extend(jQuery.fn, {
miMetodo : function() {
return this;
}
});
With this we can already give us the pleasure of writing:
jQuery('#miCosa')
.miMetodo()
.etcetera();
But to write
jQuery
(that is what we must do if we want to make sure that our plugin is compatible with other libraries), time and time again is to write a lot. We’re going to put all the code of our plugin into an anonymous function that will allow us to have an alias-safe
jQuery
.
(function($) {
})(jQuery);
And we’re going to fill it bit by bit.
(function($) {
$.extend($.fn, {
timeline : function() {
this
.bind('mousedown', handleMouseDown);
return this;
}
});
function handleMouseDown(e) {
// bla bla
}
})(jQuery);
Note: we extract the code handling the event
mousedown
. Why? We don’t need any data, local and therefore we do not need the functional closure for access to that data (or generate a function on the fly for each widget). We will do the same for the handles
mousemove
and
mouseup
:
(function($) {
$.extend($.fn, {
timeline : function() {
this
.bind('mousedown', handleMouseDown);
return this;
}
});
function handleMouseDown(e) {
$(document)
.data('timeline', {
element: this,
scroll: this.scrollLeft,
x : e.clientX
})
.bind('mousemove.timeline', handleMouseMove)
.bind('mouseup.timeline', handleMouseUp);
e.preventDefault();
}
function handleMouseMove(e) {
var data = $(this).data('timeline');
data.element.scrollLeft = data.scroll + data.x - e.clientX;
}
function handleMouseUp(e) {
$(this)
.removeData('timeline')
.unbind('.timeline');
}
})(jQuery);
With this our spaghetti-code as it seems a little more ordenadito. But a plugin without options little. We will consider the possibility of setting the ratio of displacement/drag and decide whether to hide or not the scroll bar native. By parts:
$.extend($.fn, {
timeline : function(opts) {
var config = $.extend({
speed: 1.5,
accesible: true
}, opts || {});
this
.bind('mousedown', config, handleMouseDown);
if (!config.accesible) {
this.css('overflow', 'hidden');
}
return this;
}
});
The method now
timeline
accepts an argument that corresponds to a configuration object. We use
jQuery.extend
to apply the values on a default configuration. And when you assign the handler to
mousedown
pass it as the argument. If this is new to you, what you need to know is that with an assignment of this type, the handler will receive this extra data in the property
data
the object that represents the event. Let’s modify the handler to transmit the configuration:
function handleMouseDown(e) {
$(document)
.data('timeline', {
element: this,
scroll: this.scrollLeft,
x : e.clientX,
config : e.data
})
.bind('mousemove.timeline', handleMouseMove)
.bind('mouseup.timeline', handleMouseUp);
e.preventDefault();
}
Use the settings in
handleMouseMove
:
function handleMouseMove(e) {
var data = $(this).data('timeline');
data.element.scrollLeft = data.scroll + (data.x - e.clientX) * data.config.speed;
}
And ready to go. We already have a plugin pretty decent. To get you started:
jQuery(document)
.ready(function() {
$('el.selector-de-dios')
.timeline({ speed: 2, accesible: false })
});
BALL EXTRA 1: DEFAULT SETTINGS
If our user is always going to prefer the option that is not accessible from the plugin, not for being an idiot (or obey) we are going to force to pass the settings time and time again.
$.extend($.fn, {
timeline : function(opts) {
var config = $.extend({}, $.fn.timeline.defaults, opts || {});
// ...
return this;
}
});
$.fn.timeline.defaults = {
speed: 1.5,
accesible: true
};
In this way we allow the user to configure options globally by manipulating
jQuery.fn.timeline.defaults
at your whim.
BALL EXTRA 2: MOUSEWHEEL
The plugin mousewheel allows you to control the movements of the wheel of the mouse.
$.extend($.fn, {
timeline : function(opts) {
// ...
if ($.fn.mousewheel) {
this
.mousewheel(function(e, delta) {
this.scrollLeft -= delta * config.mousewheelSpeed;
e.preventDefault();
});
}
return this;
}
});
You can try the combination of all these elements in the demo.
CODE
Choan does not invent anything new, what is covered here comes to be a combination of my skills, tastes and experience and what is explained in these two articles:
Fun with overflows, Remy Sharp.
A Plugin Development Pattern by Mike Alsup.
Keep in mind that the proposed architecture for this plugin is not always the most appropriate —we’ll talk about it— and that the sample plugin is not published or official, or extra officially, and no one will give you support.
jShoulda and JsUnitTest, unit testing in JavaScript-style Shoulda
We were talking the other day of QUnit, today we will talk about JsUnitTest –other system of testing– and of jShoulda, a layer of abstraction that a server has molded with his own hands, and in the image and likeness of Shoulda.
Principiemos talking about JsUnitTest, a port with no dependencies of the test system developed to test the prototype and script.aculo.us.
WHAT DO WE NEED?
jsunittest.js, the library of testing;
an HTML document that references.
Something like this will suffice:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Minimal JsUnitTest</title>
<script src="/js/jsunittest.js" type="text/javascript"></script>
<link rel="stylesheet" href="/css/unittest.css" type="text/css" media="screen"/>
</head>
<body>
<h1>Minimal JsUnitTest</h1>
<div id="testlog">
Los resultados de las pruebas se muestran, por defecto
en el elemento con id=testlog
</div>
<script type="text/javascript">
// aquí irá el código de nuestras pruebas,
// también podemos hacer referencia a un script externo
</script>
</body>
</html>
HOW TO WRITE TESTS WITH JSUNITTEST
To define and run the tests, we create an instance of
Test.Unit.Runner
:
new Test.Unit.Runner({});
In the configuration object that we pass as an argument, we define as properties the tests to be performed:
new Test.Unit.Runner({
testNombreDelTest : function() {
this.assert(true);
},
testOtroTest : function() {
this.assert(true);
}
});
The names of the properties corresponding to tests must start with “test”. If we want to run functions of preparation and cleaning before and after each of the tests, we use the properties setup and teardown for defining them:
new Test.Unit.Runner({
setup : function() {
this.foo = 1;
},
testNombreDelTest : function() {
this.assert(true);
},
testFoo : function() {
this.assertEqual(1, this.foo);
}
});
Notes: both the functions of preparation as the test is run in the context of an instance of
Test.Unit.Testcase
. The possible assertions are methods of that instance (that is why we use
this
).
- assert
- assertEqual
- assertNotEqual
- assertEnumEqual
- assertEnumNotEqual
- assertHashEqual
- assertHashNotEqual
- assertIdentical
- assertNotIdentical
- assertNull
- assertNotNull
- assertUndefined
- assertNotUndefined
Check what its name seems to indicate. An important detail: in JavaScript, two arrays with the same content (or two objects-used-as-hashes) can’t be compared by equality. Therefore, if you want to compare two arrays you need to use
assertEnumEqual
. And to compare two objects,
assertHashEqual
.
AUTOMATION
drnic, the human behind JsUnitTest, is also the father of newjs, a gem (if you don’t know what is a gem, ignore this section until you find out) designed to facilitate the development of JavaScript libraries. Its virtues:
- includes JsUnitTest;
- simplifies the creation of new tests;
- automates the execution of the tests.
Without going into details. Or what you see, or not see:
$ newjs milib
create
create config
create lib
create src
create script
create tasks
create test/assets
create test/unit
create test/functional
create test/assets/unittest.css
create test/assets/jsunittest.js
# etcetera
dependency install_rubigen_scripts
exists script
create script/generate
create script/destroy
$ cd milib
$ script/generate unit_test basic
exists test/unit
create test/unit/basic_test.html
$ rake test_units
(in /Users/choan/Current/tirame/milib)
Started tests in Safari
.
Finished in 2 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Started tests in Firefox
.
Finished in 1 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Skipping Internet Explorer, not supported on this OS
Skipping Konqueror, not supported on this OS
Started tests in Opera
.
Finished in 3 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
Yes, my friend, by running a rake task we run the tests on each of the browsers available on our system and we collected the results. Something that Chuck Norris is not capable of doing.
JSHOULDA
But let’s go with jShoulda, I’ve come here to talk about my book.
jShoulda is an abstraction layer for JsUnitTest that allows you to write our tests in the most pure style Shoulda. Something like this:
context('A context', {},
should('run a test', function() {
this.assert('Yay!');
}),
)();
And what advantage does this have? For a test vulgar, no, frankly. But if your tests require initializations complex, it gets cool.
context('A context', {
setup: function() {
this.foo = 1;
}
},
should('run its setup function', function() {
this.assertEqual(1, this.foo);
}),
context('which is a "nested" context', {
setup: function() {
this.foo +=1;
}
},
should('run both setup functions', function() {
this.assertEqual(2, this.foo);
})
)
)();
To use jShoulda you need to… jshoulda.js. And a HTML document as the previous one, to which you will add the reference to the library.
But to prove it you do not need to download anything. Head over to the project page. The results of tests that you will find in it (assuming that you visit with JavaScript enabled) correspond to the test of example executed in vivo. If you want to tinker, doblecliquea in the code, edit it to your liking and press the button. Suggestion: make fail the tests. Mola.