As jQuery devs know, jQuery allows easy dynamic HTML creation like this
var d = $('<div/>')
.append('some text')
.append(
$('<ul/>').append(
$('<li/>').text('list item')
.css("color", "blue")
.click(function() {
// do something
})
)
);
What's a good coding convention for creating dynamic HTML like this that can go down an arbitrary number of levels, while still being able to identify where I am in the element hierarchy and can spot if I've closed all the parentheses/braces properly?
Please don't direct me to a templating library.
Don't forget you can pass a map of properties as the second argument when creating a new element (jQuery 1.4 +):
$("<li/>", {
text: "list item",
css: { color: "blue" },
click: function(){
// do something
}
)
See http://api.jquery.com/jQuery/#creating-new-elements
Use templates like Handlebars or EJS to decouple templates from the behavior specific code.
Handlebars have an extra added feature, that is to precompile the template and convert it in a minified js. This minified js actually reduces the http calls and loads the page faster.
While in the template, you can add classes or ids to give styling mentioned in common css files.
Related
I came across a strange requirement (set by myself)...
I'm creating an easy to integrate ajax content loader plugin with lots of options and callbacks. Since the loader is a class and the developer can have multiple instances on a single page, I wanted to get rid of all the ugly code required for every single initialization and decided to use data attributes instead - they look awesome and proficient!
The question is: How to add functions and javascript in general inside a data attribute?
Example:
var url = "someurl/goes/here/";
var Template = new TemplateEngine('Name', {
onCreate: function(template, parts) {
// do something with template parts
template.ID += 1;
},
onRender: function(template, parts) {
template.addClass('flash');
}
});
var settings = {
container: DOM_ELEMENT|STRING,
template: Template,
disableDefaultRender: true,
// a bunch of hooks and callbacks like this:
onBeforeRequest: function(loader, data) {
new_data = data;
// modify request data somehow
loader.requestData = new_data;
},
onRender: function(loader, data) {
loader.renderData(data, function(part) {
// define specific rendering logic for different template parts
// in required
});
},
onAfterRequest: function(loader, data) {
},
onError: function(loader, data) {
}
// etc, etc
};
var THE_LOADER = new SuperFancyAjaxLoader(url, settings);
My original idea is to somehow put all of the above inside the said data attribute:
<div data-fancy-stuff="{all-or-most-of-the-above}">more stuff</div>
and make the script itself find all elements and initialize instances for each of them like so:
var elements = document.querySelector('[data-fancy-stuff]');
for(item in elements) {
try {
var data = elements[item].getAttribute('data-fancy-stuff');
var THE_LOADER = new SuperFancyAjaxLoader(data.url, data.settings);
} catch (ex) {
console.log('Someone messed with prototypes');
}
}
Is the idea of putting javascript functions inside an attribute idiotic? Or is there a way to actually put some js inside an attribute?
I understand that if there's so much javascript required, it's pointless to try and put it inside an attribute, but in real life cases (for this particular task), I will have 3-5 content loaders per page, most of them (or all) will use the same template and rendering logic, but they will all have to modify the request data differently by themselves.
p.s. Eval is Evil.
edit: I'm open to design proposals which do not involve third party MVC frameworks.
May be I don't understand well, but You want provide some JavaScipt modules/classes/objects through HTML5 attribute???
I think it's bad design. It's seems to be mixin of distinct layers.
So technically U have just ONE ability - to call eval, even after your PS because eval is the only point where JavaScript can get other JavaScript from String - ONLY.
But if U want dynamically load some complex javascript as reaction to data in some elements it's very good idea to learn and apply most ultimate thing for such scenarios - well-old-knownn require.js http://requirejs.org/. And if you want hardly bind DOM with some data and behavior you must to learn some of MVC JavaScript solutions - AngularJS, Backbome, Amber and so on.
By design u have to split your application to presentation layer where DOM will live and business layer were JavaScript will live. To bind them to each other you use string/JSON descriptors in DOM attribute and load JavaScript dynamically using on-fly head rewriting or by XHR+eval, such design is asynchronous, quick and is main choise of all solid network-based applications from gmail to all-other-cool-staff. To help build application with such model - require.js is best and most known helper.
server side background, getting deeper and deeper into client side.
I've got a site with a lot of legacy that I'm new to, and I'm just trying to get a handle on how things are working/what's available.
Is there a way to have jquery tell me(for a page/pages) all its current info and any plugins it can/is use/ing, similar to what phpinfo does?
Some proof of concept how you can get names for all plugins
var plugins = (function() {
var plugins = [];
for(var plugin in jQuery.fn) {
plugins.push(plugin)
}
return plugins;
}());
var filterValue = ['constructor', 'init', 'add', 'parents'] // // you must add all standard methods here
filterValue.forEach(function(value) {
var position = function(value) {
return plugins.indexOf(value);
}
while(position(value) >= 0) {
plugins.splice(position(value), 1)
}
})
console.log(plugins)
You can use the following for jQuery
console.log( jQuery.fn.jquery );
To answer your question directly, No jQuery does not have a mechanism that lists installed plug-ins.
jQuery does not keep such a registry of installed plugins. The jQuery plugin mechanism is to just add a method to the jQuery prototype (right along-side all the other jQuery methods). So, there's no separate list of which methods have been added by some outside agent (e.g. a plug-in). In addition, there's no one-to-one correspondence between methods and a particular plug-in as a single plug-in could add multiple methods.
It would be possible to create a master list (for any specific jQuery version) of what methods are there by default and then enumerate a jQuery object to find out which methods have been added since then, but you'd have to create that master list ahead of time and store it or create the master list immediately after jQuery was loaded before any plug-ins were loaded.
You can always test to see if any specific jQuery plug-in is loaded by just checking to see if any of its signature methods are available on a jQuery object.
So, if you really just wanted to know which of 10 plugins happen to be available in any given page, you could write a quick function that would test for each of the 10 plugins (by looking for the existence of known methods in those plugins) and would return a list of the ones installed, but this has to be done with specific knowledge of each plugin as there is no "generic plugin identification mechanism" since a plug-in is nothing more than a piece of code that adds methods to the jQuery prototype. It doesn't actually have any identity of its own.
I'm looking to programmatically add ng-* HTML attributes to various DOM elements. I've had some success with using $compile(obj)($scope); but this secondary compile causes issues with a number of components.
I add the ng-* attributes via jQuery... and yes, I know, directives, but this won't work for me as the ng-* HTML attributes I'm adding are boilerplate actions based on DOM structures. That and directives seem clunky (to say the least) as compared to jQuery DOM manip.
So... is there any way I can add in these boilerplate ng-* HTML attributes BEFORE Angular runs so that I can avoid the re-$compile? What I'd really love is a way to do a pre-init hook on Angular, is there such a beast?
SOLUTION:
#ChrisMartin sent me on the right path to figure out an answer to this question (thanks Chris!). What I ended up doing is this...
First I created a file named "angular-defer-bootstrap.js" that is included before "angular.js" with the following code:
//# Set the window.name to signal Angular to delay bootstrapping until `angular.resumeBootstrap()` is called.
//# See: http://stackoverflow.com/a/21049890/235704 and https://docs.angularjs.org/guide/bootstrap
//# NOTE: This MUST be included BEFORE angular*.js
window.name = 'NG_DEFER_BOOTSTRAP! ' + window.name;
I then created the following function with jQuery to preform any pre-Angular bootstrap code:
//####################
//# Setup the jQuery onDocumentLoad event to handle the pseudo-ng-directive of ng-preinit
//####################
$(document).ready(function () {
var $this, $pre = $('[ng-preinit]');
//# If we have some [ng-preinit]'s to process
if ($pre.length > 0) {
//# Traverse the [ng-preinit] attributes, eval'ing/running each and removing them so Angular doesn't freak out
$pre.each(function() {
$this = $(this);
eval($this.attr('ng-preinit'));
$this.removeAttr('ng-preinit');
});
}
//# Let Angular know it can .resumeBootstrap and remove the flag from window.name
angular.resumeBootstrap();
window.name = window.name.replace('NG_DEFER_BOOTSTRAP! ', '');
});
This is then utilized by including a ng-preinit pseudo-Angular directive/HTML attribute:
<div class="row" ng-controller="IndexController" ng-init="init()" ng-preinit="globalScope.preinit()">
The rub here is that the eval'd code contained within the pseudo-Angular directive ng-preinit has the global scope, rather than the Angular controller's $scope.
With these few lines of code, I can now cleanly hook the "pre-init" (that is, pre-bootstrap) of Angular and do whatever I like without the need to re-$compile (and it's unintended consequences), which is exactly what I wanted!
This is explained in Angular's documentation on manual initialization.
If you need to have more control over the initialization process, you can use a manual bootstrapping method instead. Examples of when you'd need to do this include using script loaders or the need to perform an operation before Angular compiles a page.
I understand that for performance reasons it is better to let the asset pipeline concatenate and minify all my javascript and send the whole lot with every page request. That's fair enough
However, a bunch of my javascript is things like binding specific behaviours to specific page elements - stuff like
$('button').click(function(e) { $('input.sel').val(this.name); }
and I would feel more comfortable if I knew that this code was being executed only on that page - not on evey other page which might coincidentally have elements with the same IDs or which matched the same selectors How do people deal with this?
I would rather not put all this stuff inline in elements, just because when it gets to be more than about two lines long, keeping javascript correctly indented inside an .html.erb file is more work than it needs to be
Here is what I do (based on some stackoverflow answers):
application_helper.rb
def body_page_name
[controller_name.classify.pluralize, action_name.classify].join
end
application.html.haml
%body{data: {page: body_page_name}}
application.js
$(function() {
var page = $("body").data("page");
if("object" === typeof window[page])
window[page].init();
});
And in appropriate js file there's an object called ControllerAction:
tickets.js
var TicketsShow = new function() {
var self = this;
self.init = function() {
// code which may call other functions in self
};
};
There's probably better way to do it, but this works for me
I'll describe what I currently do, just in case it gives anyone a better idea
1) I changed the 'body' tag in my application.html.erb to add the current controller and action as data- attributes
<body data-controller="<%= controller.controller_name %>"
data-action="<%= controller.action_name %>" >
2) I test this at the top of the relevant javascript
$(document).ready(function() {
if($('body').data('controller')=='stories') {
$('.story').click(function(e) {
var u=$(this).data('url');
u && (document.location=u);
});
}
});
I can't decide if I think this is a good idea or not
For page specific JavaScript, I typically do something like this:
Application Helper
In the application helper I create a class attribute (though you could just as well use a data attribute instead).
module ApplicationHelper
def body_attributes
controller = params[:controller].gsub('/', ' ')
action = params[:action]
version = #version ? "version_#{#version}" : nil
{
class: ([controller, action, version] - [nil]).join(' ')
}
end
end
Note I'm also adding a version string. This helps with Google content experiments, and makes A/B testing a breeze.
Application.html.haml
In my global layout file, I do something like this to insert the attributes on the body tag:
!!! 5
%html
%head
...
%body{body_attributes}
script.js
Now in my page specific script, I just check for the class attributes, like this:
$(function () {
if ($('body.pledge.new, body.pledge.create').length > 0) {
// do work here...
}
});
The advantage of this method is that getting the body by class is very quick. The script inside the conditional will not be executed at all on any page apart than the ones I choose, so minimal overhead, and I don't need to change my selectors throughout the code.
EDIT
Note that this answer is now 3 years old. You should be using client-side routing with a framework like React instead.
I'd add a class to the BODY tag, allowing you to identify each page, and therefore each control per page.
<body class='page1'>
JS:
$('.page1 button').click(function(e) { $('input.sel').val(this.name); }
I've done it and seen it done in several different ways:
Rigging up the mvc to be able to load a particular js file per page, named along the same lines as a controller file. Like: <controller-name>.js
Making a url parser in JS and then setting a global variable to the current page: UrlParams.currentView = 'dashboard'; and then saying if(UrlParams.currentView == 'dashboard') { //do specific js here }
Setting a unique identifier as the page class or ID and then targeting that with your JS selectors. $('#dashboard').xyz();
I am a web guy doing mostly Perl server-side stuff, and I'm slowly coming to a few conclusions.
It is far better to do most of your code via Javascript and toss data back and forth via AJAX than it is to hit submit and reload a mostly-identical page
I like jQuery because I like CSS, and it's fun to chain together big long and scary-to-others definitions
There's something to that templating stuff.
You want your HTML elements to look like your HTML elements, and it's easier to define that in HTML:
<div class="sidebar_elem">
TEXT
</div>
Than it is to cobble up the same in Javascript or jQuery:
( '<div/>' )
.attr('id' , 'sidebar_elem' + i )
.addclass( 'sidebar_elem' )
;
( '<a/>' )
.attr('href' , link_url )
.appendTo( '#sidebar_elem' + i )
;
This is to say that I am no longer a templating agnostic, but I don't know which templating tool to believe in. I have looked into some jQuery-based templating plugins, but I have yet to become happy with any of them, in part because the ones I've seen seem to want to put all that code into the page itself, which breaks the "Only markup goes into HTML files, only style goes into CSS files, only code goes into JS files" mantra I keep reciting.
So, I'm looking for a Javascript-based templating tool that would allow me to have my templates in an outside file so I can have one template change cover a series of web pages. If it's jQuery-based, that's great, less stuff I have to learn, but it isn't a deal-breaker.
There are several good ones out there:
Mustache.js
Pure.js
Json Template
If you want a jQuery version, Tempest looks good.
The 2 libs I know that do not mix template coding with HTML markups are chain.js and PURE
chain makes only DOM manipulations.
PURE uses a mix of DOM and innerHTML as the DOM alone can be slow to render bigger templates.
I'm the main contributor of PURE, and we created it to build a web app on the ajax model you describe.
Take a look at this one http://ejohn.org/blog/javascript-micro-templating/. Made by John Resig, creator of jQuery, this one doesn't even need jQuery, and it's freaking small. It also stores templates in script tag (Daniel's answer). Example template:
<script type="text/html" id="user_tmpl">
<% for ( var i = 0; i < users.length; i++ ) { %>
<li><%=users[i].name%></li>
<% } %>
</script>
Maybe you can load them using src attribute if you really need them to be in separate files, which I don't think is a wise idea, because it means additional roundtrip to the server. So at the end, for the sake of optimization, you can store them in separate files, but embed them server side in the page that needs them.
Since there is no well defined API and a best library for templating, I would suggest you choose one that is actively developed. Below, I briefly explained two libraries that are actively being developed.
jQuery team decided that jQuery Templates will no longer be actively developed or maintained, thus I would strongly suggest NOT using it. See this blog entry.
You can use JsRender in accordance with JsViews to take full functionality provided by jQuery Templates and even more like data linking. You can find demos for JsRender and JsViews. I would suggest using these libraries since they are actively being developed by jQuery UI team but be aware that they are still not even beta!
Mustache is another templating solution that is actively being developed and it simplifies templates by combining conditional tags and enumeration tags. It also provides strong features like inverted sections, partials and high order sections with simple syntax. Mustache is also one of the fastest templating solutions See blog by Brian Landau. I, personally, find mustache a good templating solution since it has a simple syntax and performs well.
How about EJS?
Example from their page:
"EJS combines data and a template to produce HTML."
Data:
{title: 'Cleaning Supplies', supplies: ['mop', 'broom', 'duster'] }
Template:
<ul>
<% for(var i=0; i<supplies.length; i++) {%>
<li><%= supplies[i] %></li>
<% } %>
</ul>
Result:
mop
broom
duster
You should check out google closure template. It's completely independent so you can use it with any lib you want. It's a templating tool written in java.
http://code.google.com/closure/templates/docs/helloworld_js.html
It allows you to create a template on the server, run a java "compiler" on it and the output is a javascript function that takes json as its parameter.
{namespace examples}
/**
* Greets a person using "Hello" by default.
* #param name The name of the person.
* #param? greetingWord Optional greeting word to use instead of "Hello".
*/
{template .helloName}
{if not $greetingWord}
Hello {$name}!
{else}
{$greetingWord} {$name}!
{/if}
{/template}
This will generate a function called examples.helloName that can be called like
Their format is very IDE friendly, I get all the HTML syntax highlighting when editing the templates
examples.helloName({name: 'Ana', greetingWord:"Howdy"});
You can call other templates from within templates, it automatically html escapes your data (unless you tell it not to), provides bidirection support.
Another great thing is the fact that the templating tool can also generate java code. So somebody writing an app that must support browsers with scripting disabled can generate the HTML on the server if necessary.
Last but not least, unlike other js templating systems (), the template is parsed on the server, so the client side only has to do the merging of the template and data, the parsing of the template is done as a build step on the server.
http://dev.sencha.com/deploy/dev/docs/?class=Ext.XTemplate is an example of a templating tool that runs completely on the client. There are two problems with this approach, the parsing of the template is done on the client and your html has to be embedded in a javascript string. However, some IDEs (Intellij) will highlight the HTML inside JS strings).
What about JAML Code?
http://github.com/edspencer/jaml
Similar to a few of the above but I believe is a bit more logical...
It is the concept of defining your templates via JSON / JavaScript and then using a function in JavaScript to pass arguments to your template which gets it rendered and returned as an element.
There are implementations around for the various JavaScript Frameworks that exist.
Try out this:
https://www.npmjs.com/package/jlate
use CDN:
<script src="https://cdn.jsdelivr.net/combine/npm/lodash,npm/jlate#0.0.2/jlate/JLate.min.js"></script>
I given below working example where you can replace github template url with your own template sample:
https://raw.githubusercontent.com/webphonix/JLate/main/test_project/template/weblate_loop.html
<div class="row">
<% _.each(names, function(n){ %>
<div class="col-md-6"><%- n.name %></div>
<% }) %>
</div>
var author = [{
name: "Guru"
},
{
name: "Gurudev"
},
{
name: "Test"
},
{
name: "Webphonix"
},
];
$$("#my_temp").jlate({
names: author
});
<script src="https://cdn.jsdelivr.net/combine/npm/lodash,npm/jlate#0.0.2/jlate/JLate.min.js"></script>
<div>
<jlate id="my_temp" src="https://raw.githubusercontent.com/webphonix/JLate/main/test_project/template/weblate_loop.html" type="template">
Loading...
</jlate>
</div>
Try JLate:
https://www.npmjs.com/package/jlate
use version 0.0.2 instead 0.0.1
use below cdn:
<script src="https://cdn.jsdelivr.net/combine/npm/lodash,npm/jlate#0.0.2/jlate/JLate.min.js"></script>
Use a script block.
<script id="someId" type="text/html">
<!-- your template here -->
</script>
and one of many JQuery plugins.
http://weblogs.asp.net/scottgu/archive/2010/05/07/jquery-templates-and-data-linking-and-microsoft-contributing-to-jquery.aspx
I have a templating engine called stencil.js, which I believe is pretty sweet. It works with jQuery via the jquery-haml DOM building engine.
Write your template (which you can put in an external file and decode as JSON):
["%div.sidebar_elem"
["%a", { href: { key:'link' } },
{ key: "text" }
]
]
And run it through stencil along with your data:
$("#parent").stencil(template, { link: "http://example.com", text: "Click me!" });
There are more examples at the stencil.js GitHub project, but I think it's just what you're looking for.
It could use a couple more utility methods, and some code for an unfinished data binding component is still in the master branch, so drop me a comment if you’re interested and I'll see if I can clean it up.
check out ibdom, and some background/history here: Recommended JavaScript HTML template library for JQuery?
Could always go with jQuery-Templates: http://api.jquery.com/category/plugins/templates/
What about http://www.enfusion-framework.org
Stuff like this:
<span template>Our telephone number is {phone}.</span>
<span session>You are logged in as {nickname}.</span>