AJAX
NinjaScript: JavaScript so unobtrusive, you won't see it coming.
Summarized using AI

NinjaScript: JavaScript so unobtrusive, you won't see it coming.

by Evan Dorn

In this video presented at LA RubyConf 2011, Evan Dorn introduces NinjaScript, an unobtrusive JavaScript framework built on jQuery designed to simplify the integration of JavaScript with web applications while ensuring graceful degradation for users without JavaScript enabled. The presentation outlines several key goals and features of NinjaScript:

  • Unobtrusiveness: NinjaScript allows developers to enhance websites semantically without adding extra markup for JavaScript behaviors. This is crucial for maintaining a clean and accessible HTML structure.

  • Graceful Degradation: The framework promotes the importance of maintaining website functionality when JavaScript is turned off. Dorn emphasizes the need for websites to still provide a comprehensive user experience for those utilizing screen readers or browsing with JavaScript disabled, helping to represent functionality accurately to search engines and automation tools.

  • Persistent Behavior: A major theme of NinjaScript is the concept of automatic rebinding, which allows built behaviors to be applied consistently even when new elements are dynamically added to the DOM. This overcomes limitations typically faced with traditional event binding in JavaScript, where newly created elements may not retain their assigned behaviors.

Dorn also highlights several predefined behaviors available in NinjaScript, such as converting forms into AJAX submissions, implementing decay effects for elements, and creating watermark inputs for forms. He mentions that developers can define their own behaviors, allowing for reusable code that enhances the development process.

In addition, the video discusses the challenges addressed by Mizugumo, a gem that works alongside NinjaScript, especially in creating AJAX-friendly Rails applications without additional markup or JavaScript overhead. Mizugumo ensures that links and forms remain functional regardless of the presence of JavaScript, providing intuitive user interactions.

The demonstration showcases a Rails project using both NinjaScript and Mizugumo, displaying features like AJAX functionality, graceful degradation, and user prompts for deletion actions. Dorn stresses the framework’s user-friendly nature and its potential for future enhancements, including better integration with various JavaScript environments beyond jQuery.

To conclude, the main takeaways from the talk include the importance of unobtrusive JavaScript, maintaining functionality across different environments, and the utility of predefined and custom behaviors in enhancing web application development. Contributions from the community are welcomed in this open-source project, encouraging ongoing improvements in NinjaScript.

00:00:08.880 Hello everyone.
00:00:30.880 I'm here to talk about NinjaScript. This is our exciting new project. I'm not talking about a tool you already use every day; NinjaScript is a tool that was created by Judson. Judson, could you raise your hand? Let's give a round of applause to Judson.
00:00:50.239 As I mentioned, I am the CEO and lead developer of Logical Reality Design. However, this tool has been almost completely coded by Judson.
00:01:01.039 NinjaScript is a JavaScript framework with three main goals. The first goal is to be completely unobtrusive. When you're using Rails, Rails 3 has made significant improvements in unobtrusive JavaScript compared to Rails 2. However, with NinjaScript, you can enhance this further. You can create completely semantic HTML sites with no additional markup needed for any of your JavaScript behaviors.
00:01:24.159 The second goal is to ensure that our websites degrade gracefully. What does this mean? It means that you should have a fully functional website that uses JavaScript to enrich the experience. More specifically, your website should still work without JavaScript.
00:01:36.479 How many of you have written websites that fail to function when JavaScript is turned off? Quite a few, I see. Let's admit it, the Rails world has significant challenges in this regard. It's not entirely our fault, as this is a difficult problem, and Rails doesn't make it any easier.
00:01:59.200 However, we should care about graceful degradation for several reasons. For example, Google spiders and various automation tools will access your website with JavaScript turned off. You want to accurately represent your website's functionality to your search engines and other automation tools. Additionally, individuals who are blind or have poor vision rely on screen readers that typically function without JavaScript.
00:02:16.879 Furthermore, there are security concerns. Users who run NoScript or disable JavaScript will browse your website, and if it doesn't function without JavaScript, you lose a significant portion of your potential audience. It’s important to recognize that creating something effective without JavaScript can feel like duplicating your work, especially if you are writing AJAX controllers.
00:02:41.120 However, the goal of NinjaScript is to make this easier for you. The third goal of NinjaScript is to provide persistent behavior. This concept may be less obvious. We're used to how JavaScript works, but we often overlook that JavaScript does not provide persistent behavior. The metaphor I like is that CSS is fantastic, but JavaScript has its limitations.
00:03:06.159 In CSS, you use selectors to apply styles, and these styles consistently apply to the DOM nodes that meet the selectors' criteria. If you change your DOM, for example, by having a certain class with a yellow background, any new element added with that class will keep that background as yellow. Unfortunately, JavaScript doesn't operate this way because it has state.
00:03:18.640 Let’s imagine you have a DOM structure with a div containing the id 'content' and two links with the 'tooltip' class. If you use a little jQuery code to assign behaviors to those elements, you can enhance them. But if another widget comes along later and adds another element with '.tooltip', it won't have your bound event handlers, leading to inconsistent behavior in your app. This inconsistency is frustrating and stems from when JavaScript initially binds these behaviors.
00:03:40.880 Now, many of you might be thinking we could use event delegation. jQuery provides the live method, which allows us to bind handlers to the root of the DOM instead of individual elements. As unhandled events bubble up, they reach the root, which can handle all events. This is indeed a useful feature.
00:03:51.840 Unfortunately, event delegation struggles with transformations of elements that are dynamically added to the page. For instance, you may want to make all your divs of a certain class have rounded corners by adding new structure to the HTML. However, if the div meant to get rounded corners is added late in the process, there won't be an event to trigger that behavior.
00:04:11.120 We should be able to catch this with event delegation, as DOM Level 2 specifies mutation events for things like subtree modifications. However, Internet Explorer never implemented these events. As a result, DOM Level 3 has deprecated these events, which is a frustrating situation, especially since they have not provided replacements.
00:04:40.800 Consequently, we have employed a different approach known as automatic rebinding. Instead of binding directly to elements at initialization, we examine the DOM continuously to identify new elements to which our behaviors should bind.
00:05:04.320 We utilize deprecated events where applicable, and also implement other approaches like firing our own events in browsers that require additional help. This method works most effectively.
00:05:14.560 So how do you use NinjaScript? We aim to simplify your experience compared to the typical complexities associated with JavaScript. We define something resembling a CSS sheet, which we call a behavior sheet.
00:05:45.440 In your application.js file within your Rails project, you will notice some code that opens a behavior block, identifies CSS selectors, and specifies relevant behaviors. These behaviors are predefined in NinjaScript. For example, you can take a form with the id 'product' and convert it into an AJAX form with no additional HTML required.
00:06:21.440 Similarly, you can apply behavior to links and form inputs, such as watermarking them or setting sections of the page to disappear after a specified time period. Although we don’t currently have a tooltip behavior ready, it is in the works.
00:06:29.120 In addition to predefined behaviors, you can define your own. Following the selector in your behavior definitions allows you to create a behavior object, so that any new element with the class '.awesome' added to the page, regardless of timing, will have this behavior applied.
00:06:38.480 You can also define event handlers that will be applied to these '.awesome' elements, such as click handlers and mouseover handlers. This is great because NinjaScript’s internal structure means you won’t need to worry about when elements are added to the page.
00:06:51.120 Any behaviors will take effect for elements already on the page when the JavaScript loads, and likewise, they will also apply to any elements added afterwards.
00:07:01.919 We also provide shortcuts for convenience. In many cases where you have just a single event handler, you can simply specify the event directly after your selector, like '.awesome click'.
00:07:18.560 You can define your own reusable behaviors similar to how we have prepackaged behaviors, such as turning form submissions into AJAX requests. This involves wrapping code in boilerplate, which can be found in the documentation, allowing you to define a function that returns a Ninja behavior.
00:07:26.560 This behavior will manage transformations, clicks, mouseovers, and so on. Once defined, you can reuse this behavior freely throughout your application.
00:07:38.240 We aim to simplify the experience for developers, allowing you to focus on building your applications without worrying about the intricacies of JavaScript workflows.
00:08:05.440 Here are some of the predefined behaviors we currently have available: 'submits as AJAX', which converts existing forms and links into AJAX-compatible versions. This behavior retains all defaults for graceful degradation. If you create a form that functions properly, you can later adjust it to also work as an AJAX form.
00:08:29.040 You can also convert forms into links. This is particularly useful for situations like deleting a resource in a Rails application, or executing non-GET methods. By writing a form with a delete button, 'run becomes link' converts it into a link that still performs as expected.
00:08:37.920 We also have decay behaviors that fade elements away after a specified period, and you can customize the time it takes for that decay. Additionally, 'is watermarked' converts forms with labels into watermarked inputs where the label text acts as placeholder text.
00:09:09.680 Turning off JavaScript still allows an effective experience, as non-JavaScript users see the label and can understand the text field. When JavaScript is enabled, the label disappears, leaving only the text in the input field.
00:09:34.560 We have additional behaviors planned for future versions of NinjaScript. However, it’s important to note that NinjaScript currently depends on jQuery version 1.4.2. There are technical reasons for this, which I can discuss during the Q&A if you have concerns. This means that it does not support more current versions of jQuery, but a fix is on its way.
00:09:49.760 Our future plans involve completing the prepackaged behaviors and decoupling NinjaScript entirely from jQuery. Currently, NinjaScript functions as a jQuery plug-in, but in the upcoming major version, it will operate as its own environment. You’ll be able to write behaviors in MooTools, Prototype, or any framework you prefer while utilizing either Ninja’s or jQuery’s persistence framework.
00:10:04.480 For now, it handles all AJAX calls by defaulting to Rails' return formats, but we plan to establish frameworks that enable you to write AJAX behaviors returning data in JSON or even XML formats, aligning with NinjaScript’s AJAX processor.
00:10:33.200 Judson has dedicated significant effort to this project, and while it may seem straightforward from the outside, the internal complexities are quite intricate. For instance, what occurs when multiple behaviors affect the same DOM object? Judson has established sensible defaults and configuration options to manage behaviors' execution order.
00:10:48.280 Moving on to the second half, we discuss Mizugumo. Mizugumo is a gem designed to assist you in using NinjaScript within your Rails applications, enhancing them to work seamlessly with AJAX and JavaScript behaviors. After installing Mizugumo, you run a generator, which sets up an application that is AJAX-friendly by default.
00:11:13.360 Mizugumo addresses what I refer to as the 'Rails 3 REST fail.' We know that REST works in Rails by simulating PUT and DELETE methods since browsers only support GET and POST. This is achieved by adding a hidden variable to forms that indicates the intended method.
00:11:22.560 Similarly, Rails allows specifying methods in links by adding a data-method attribute using Rails' JavaScript. However, if JavaScript is disabled, this results in linking that acts like a show link, failing the intended delete action and creating a frustrating experience.
00:11:38.960 Mizugumo solves this by transforming the link into a form if you specify a method argument. It generates a button with the labeled action, like 'delete this item.' If you haven’t passed a method, Mizugumo defaults to using the standard Rails helper.
00:11:51.360 Additionally, there’s a default NinjaScript behavior included in your application.js, looking for this specific class to convert it into a link. This means JavaScript users see a link, while non-JavaScript users see a button, ensuring full functionality without requiring additional developer effort.
00:12:05.200 Now, as with AJAX, creating forms in Mizugumo requires no additional markup. You can create forms, combine them with data, and specify methods without having to declare them as remote. You simply need to implement a respond-to block in your controller with a corresponding js.erb view.
00:12:14.080 Fortunately, Mizugumo includes scaffolding support to handle this efficiently. Let’s look at a demonstration.
00:12:19.920 I'm going to generate a new Rails project here, although I can't see it on the screen in front of me.
00:12:27.600 Let's proceed with creating a scaffold. We'll work with recipes, including attributes like name, cook time, and instructions. This is just a typical scaffold generation—nothing different from the usual process.
00:12:51.520 After generating the scaffold, we’ll migrate the database and launch the server to see how it functions. Yet, I want to add an extra touch, so we can observe how it operates with AJAX.
00:13:04.640 Let’s open the newly created index file, and I’ll insert a timestamp at the top of the page, so we can track when it loads.
00:13:12.640 Watch the last two digits here.
00:13:28.480 With JavaScript enabled, we can seamlessly create new entries and modify the page without needing to reload. The application will respond to user actions, all done dynamically here.
00:13:38.960 Notice the delete operation; even with JavaScript disabled, the application continues to function, albeit with complete page reloads.
00:13:45.120 Turning off JavaScript shows we receive the familiar button interface without losing functionality. This allows for graceful degradation.
00:14:00.160 Yet, let’s explore a few additional features that Mizugumo and NinjaScript bring to the table. This demo showcases a variety of behaviors we've incorporated into our application.
00:14:15.440 We’ve implemented sensible defaults across NinjaScript functionalities. For example, with AJAX requests, users typically receive little indication of processing, meaning you would need to implement your own spinner or loading indicator.
00:14:34.560 Hence, we modified the 'submits as AJAX' behavior to automatically apply a spinner overlay when a request is being handled. It takes very little effort to customize the placement of the spinner, and by default, it overlays above the form.
00:14:44.640 During testing, I’ll simulate longer server responses, just as you might observe in real-world applications. I’ll showcase how easily it handles these interactions.
00:15:00.640 What’s interesting is how the system responds to async behavior while maintaining clear readable indicators to users.
00:15:06.480 The above behavior greatly enhances user experience during potentially long operations.
00:15:12.879 Moreover, we have confirmation behaviors that allow users to receive deletion prompts before proceeding to remove an item. This integrates straightforwardly within the default action states, ensuring transparency in the process.
00:15:21.200 Therefore, even if JavaScript is disabled, users still receive graceful, intuitive interactions through straightforward forms.
00:15:44.920 In the future, we anticipate enhancements to completely replace Rails.js with a bespoke NinjaScript version of the default Rails behaviors—allowing development with only one JavaScript engine.
00:15:58.080 Also, we intend to refine our scaffolding generators, enabling them to produce tests or specifications right from the outset. This will ensure both AJAX and non-AJAX behaviors are adequately updated.
00:16:06.880 Our aim for user-confirmed actions like deletions is to foster prevention strategies while preserving the existing structure and functionalities.
00:16:15.460 Furthermore, we are focused on automatic JavaScript validations to align with Rails validations, ensuring no invalid input can be submitted without alarming the users.
00:16:23.040 Contributions are always welcome, as this is an open-source project. We encourage you to follow our progress on GitHub and to assist us in improving NinjaScript.
00:16:30.320 Do you have any questions?
Explore all talks recorded at LA RubyConf 2011
+5