Posts tagged javascript
Posts tagged javascript
(this is a code heavy post, with assumed familiarity with javascript, haskell, and the heist templating library, as a warning)
The motivation of the code / method outlined in this post is rather simple: I would like to encode dynamic aspects of the websites I write in the markup, not in Javascript code (corollary to this: I don’t like having lots of code running in javascript, and prefer writing things declaratively to imperatively, preferably with the aid of a type system, but I do want the sites that I build to have dynamic behavior. This solution is an attempt to reconcile these two things).
This may seem impossible, but it actually turns out to be rather simple. The key is to identify a small set of dynamic primitives that should be available, with clearly defined markup that should result in predictable (preferably local, to make it easier to reason about / check correctness) effects.
Then, global event listeners will look for certain events that are triggered on the specified markup, and will perform the needed operation. This means that you will code the effect once in Javascript, which will effectively extend the dynamic abilities of your markup, anywhere where the small javascript primitives are available.
Once the necessary javascript has been described, built, and tested, using Heist I will build Splices (which are special html tags that generate html based on their attributes, children, etc) that will produce the markup that the javascript needs to recognize / carry out the behavior, while exposing a very simple interface to the user (this reduces the chance that the user, ie, me and others working with this code, could generate markup that would screw up the handlers). From the user’s perspective, these special tags ARE the dynamic behavior, and provide a reliable way of ensuring that the effects always work.
The reason for creating this was rather simple: For a project, the designer in my team wanted to have input fields that, when selected, popped up a box that presented options to select by clicking on them. This is the task that usually a select field would fulfill, but it did not fit with the interface, and it was something that had come up before (when we did settle on a plain old select field) so I set out to create a way of consistently creating this pattern. One such end-use case is shown here:
<box-field name="person-name" value="$(person-name-value)">
<people>
<box-option value="$(personId)">
<personName/>
</box-option>
</people>
</box-field>
The “box-field” and “box-option” are splices that are globally available. They are what encode the idea of this Box Field (a field that presents options in a box to pick between). name is what the field’s name will be when the form is submitted, value is the prefilled value (here dynamic). The “people” splice is what holds the data that is populating the options (and it presents the subSplices “personId” and “personName”) - don’t worry about that for now, as it just as easily could have been a static list of box-options, like:
<box-field name="person-name" value="$(field-name-value)">
<box-option value="1">
Jane
</box-option>
<box-option value="2">
John
</box-option>
<!-- ...etc -->
</box-field>
Since this element might be used in different places, and on fragments of pages returned asynchronously, I did not want to have to make sure I was always attaching the proper handlers, every time the element was created. Having to always attach handlers also runs counter to the idea of encoding the behavior in the HTML - I want declarative dynamic behavior, not imperative.
So after looking a little, I figured what I needed was a way to register a global event listener that would listen for a specific event, match a certain element, and then run a corresponding function on it. I have used the lightweight standalone javascript libraries by @dedfat (of twitter), because they are simple and work well for me, but this could easily be adapted to use jQuery, etc. The function I came up with is “declare” - which takes an event, a selector, a boolean as to whether it should stop propagation (I think the answer will always be yes, but wasnt sure), and then the function to call when this occurs, passing in the element.
/*!
* declarative.js - copyright @dbp 2011
* BSD3 License
*/
function declare(event, selector, nopropagate, fun) {
// bean is an cross-browser standalone event handler
// at https://github.com/fat/bean
bean.add(document.documentElement, event, function(e) {
e = e || window.event;
var elem = e.target || e.srcElement;
// qwery is a cross-browser selector engine
// at https://github.com/ded/qwery
if (!elem || qwery(selector).indexOf(elem) === -1) {
return;
}
fun(elem);
if (nopropagate) {
e.stopPropagation();
}
});
}
Then I came up with the following HTML that I wanted to govern my box-field, the idea being that the hidden input field would contain the person’s ID when it is selected, and that the display field is what will show the name that is selected (styling done inline here, just to keep it self contained):
<div class="box-field ">
<input type="hidden" name="person-name" value="">
<div class="display"
style="width: 200px; height:20px; border: 1px solid black;">
</div>
<div class="box" style="display:none;">
<div data-box-value="1" class="option ">
Jane
</div>
<div data-box-value="2" class="option ">
John
</div>
</div>
</div>
Then I wrote up the handlers to make this box-field actually work:
bean.add(document, 'DOMContentLoaded', function () {
declare("click",".box-field .display",true,function (elem) {
// bonzo is a cross-browser selector engine
// at https://github.com/ded/bonzo
bonzo(bonzo(elem).next()).show(); // show the box
});
declare("click",".box-field .box .option",true,function (elem) {
bonzo(elem.parentNode).hide();
d = bonzo(elem.parentNode).previous()[0];
d.innerHTML = elem.innerHTML;
bonzo(bonzo(d).previous()[0]).attr("value",
elem.getAttribute("data-box-value"));
});
});
Finally, the splices that turn the markup shown at the beginning of this post into the html that the javascript actually operates on:
import qualified Text.XmlHtml as X
import qualified Data.Text as T
import Text.Templating.Heist
boxField :: Monad m => Splice m
boxField = do node <- getParamNode
case X.getAttribute "name" node of
Nothing -> return [] -- without name, useless
Just name -> do
let klass = T.concat
["box-field ",
(fromMaybe "" $ X.getAttribute "class" node)]
let value = fromMaybe "" $
X.getAttribute "value" node
let children =
[ X.Element "input"
[("type","hidden"),
("name",name),
("value",value)] []
, X.Element "div"
[("class","display"),
("style",
"width: 200px; height:20px; border: 1px solid black;")] []
, X.Element "div"
[("class","box"),
("style","display:none;")]
(X.elementChildren node)
]
return [X.setAttribute "class" klass $
X.Element "div" (filter ((/= "name").fst) $
X.elementAttrs node) children]
boxOption :: Monad m => Splice m
boxOption = do node <- getParamNode
case X.getAttribute "value" node of
Nothing -> return []
Just value -> do
let klass = T.concat
["option ",
(fromMaybe "" $ X.getAttribute "class" node)]
let attributes =
("class", klass) : (filter
((flip notElem ["name","class"]).fst) $
X.elementAttrs node)
return [X.setAttribute "data-box-value" value $
X.Element "div" attributes
(X.elementChildren node)]
There is obviously a bit of styling that still needs to be done to make the box look like it should, but everything from here on out (and any number of uses of this box) should be outside of the world of javascript - which was my intention in the first place!
PS. I just realized a small flaw in the example I presented. The splice “box-field” should also have a “display” attribute that has predefined what the field should show (to match up with the predefined “value”). This is easy to do, and should not detract from the presentation.
I’ve recently started working with Snap, the Haskell web framework, (http://snapframework.com), and one reason (among many) for my reason to switch from Ocsigen, a web framework written in OCaml (which I’ve written posts about before) was the desire to more flexibly handle ajax based websites. While it seems good in some ways, I eventually decided that Ocsigen’s emphasis on declaring services as having certain types (ie, a fragment of a page, a whole page, a redirect, etc) is in some ways at odds with the way the web works.
After starting to work in Haskell again, and with the Snap team authored templating system Heist, I immediately began looking for ways to work with ajax content more flexibly than I had been doing before. Inspired by the work of Facebook on Primer (provided to the world at https://gist.github.com/376039 ), which is their base-line system for dynamic content - basically, event listeners waiting for onclick events on links that have a special attribute that says it should perform an ajax request, and event listeners for onsubmit events on forms that have a special attribute that indicates the forms should be serialized and submitted asynchronously. But even more interesting than that (to me) was the other half of their system (not, I believe, public, and regardless, written in PHP), which is that the server side response decides what client side div’s it should replace.
At first that sounds a little dirty - it basically entails mixing (conceptually) server code and client code. But then it allows a different sort of methodology - that even with client side modifications, it is the server that ultimately has all control - including what to replace on the client. This is a fascinating idea, because clientside code is notoriously limited be being written in javascript (or with javascript libraries), and thinking about having to maintain clientside and serverside code seems to be a much dirtier solution than having the server, in short, control the client.
Taking this idea, and bringing it into the world of Heist, which is (in my opinion) a fantastic templating system (more info at http://snapframework.com/docs/tutorials/heist ), ended up being quite straightforward, as Heist lends itself to the idea of extending the syntax of html, much like the facebook primer system did.
At first I thought that there should be haskell code that would specify things like “replaceDivsWithSplices …” where div’s would be identified and corresponding splices (things that can be inserted into heist templates) would replace them, and then “replaceDivsWithTemplates”, etc, but the whole solution seemed a little off.
And then I realized that the entire idea could be summed up with a single tag: “div-async”. The idea would be, this would be a special div that could foreseeably be replaced by an asychronous response. A template would have many divs that were marked this way, which in a non-async response would do nothing special, but when an async response came back, all div-async’s would replace corresponding tags on the page.
The only things that remained were the two tags to start the async requests, which I named “a-async” and “form-async”, and a little javascript to make the moving parts work together. And so, heist-async was born. (for the impatient, the code exists at https://github.com/dbp/heist-async , and while I am using this code currently and it seems to work, it could change significantly as things are worked out)
The basics of how this works should be obvious, but I can illustrate a basic example. On a page you have an announcements box. You want the user to be able to click a button and have the announcements box reload without reloading the whole page (new announcements may have occurred). So you have a page template that looks like this:
<html><body><h1>Some page</h1>
<apply template="announcements></apply>
<a-async href="/recent_announcements">Reload</a-async>
</body></html>
And an announcements template that looks like this:
<div-async name="announcements">
<announcements>
<text/>
</announcements>
</div-async>
Now to glue this together, all you need to do is serve the original page (with the proper splice set so that the
Now that is pretty cool - what it means is that you can have one set of templating code, and the only change you need to do is separate any parts you want to be able to load asynchronously into separate templates, and make sure there is a div-async wrapper around it. (NOTE: since I didn’t mention it before, it might be helpful to now - div-async is just a regular div, so you can set all the regular things, like id, class, etc. Also feel free to take existing div’s and just add -async and set a name).
At this point, I was pretty happy with this, and thought it was working pretty well, but of course the real world is much more complicated, and not everything is so simple - sometimes a single asynchronous request should mean a lot of different things on a page should change. In this case, it is possible that the simple template inheritance will not work, but with the addition of a template that is just for the response, that includes all the templates that should be updated, it seems to work pretty well. An example of one of these could be:
<apply template="announcements"></apply>
<apply template="title"></apply>
In this case, there is still no duplication of formatting code, all that exists now is an explicit list of all the parts of the page that should be replaced by a given request.
Other common things; to hide an element, sending back:
<div-async name="something" style="display:none"></div-async>
Should work. You could also put some empty placeholder div’s like that on a page, and later replace them with ones with actual content.
What I noticed about this is that it makes dynamic page changes very explicit in the templates, which I think is a very good thing - and certainly makes it easier to reason about page changes.
Getting to this point, I started using this to implement a bunch of parts of a new site I’m working on, and I was happily impressed by how it all seemed to be working. Using this, it seems like ajax can be thought of as just an aspect of the templating system - describe what should be replaced, and it will be, without ever having to worry about the clientside code (which is 12k of lightweight libraries and 60 significant lines of code of custom javascript. The 60 lines should easily be able to be translated to depend on common javascript libraries like jQuery, I just didn’t want to make that a requirement).
I’m interested in feedback on the library, and ways that it can be improved. It is still very early software (a week ago, it did not exist), but it is something that I’ve found very powerful, and I’m kind of interested in where it can be taken / what people think about it.