The effective way to call javascript initializing form? - javascript

One often have form with some dynamic parts, that needs to be initialized onload. E.g. datepickers, enhanced selects, section toggling, hiding/showing conditional elements etc.
Example:
<form>
<input type="text" name="date">
<select name="selection"></select>
</form>
and I want to init datepicker on the date element and Select2 on the selection element.
Where to put the form initialization?
My thoughts:
Init throught global selector:
$(function() {
$('input[name=date]').datepicker();
$('select[name=selection]').select2();
})`.
But I have one js file for the whole web, so this would led to crawling the whole DOM on each page load, even if the element is not present on current page.
Some kind of conditional selector. E.g. give <body> and id and add to my global js file something like this: $(function() { $('input[name=date]', 'body#foo').datepicker(); })
Encapsulate the init for each form into a function (or class method), and call the function from HTML:
<script type="text/javascript">
$(document).ready(initMyForm());
</script>
But I'm guessing, isn't there any better way? What would you suggest, especially for bigger projects with many different forms requiring different javascript initialization?

If for your current project you are running one JS file, or even for medium-size projects where a 'generic' form setup function is appropriate, using a function as you described would be appropriate.
See example below, with the function wrapped as a small jQuery plugin, so you can call this on specific selectors as required, to avoid running through the whole DOM.
;(function($){
$.extend({
initMyForm : function(){
$(this).find('input[name=date]').datepicker();
$(this).find('select[name=selection]').select2();
}
});
})(jQuery);
So you can use this like:
$(document).ready(function(){
$(body).find(form).initMyForm();
});
$("#my-form").initMyForm();
$(".page-content .form").initMyForm();

Related

html, js - how to limit elements "scope" - namespaces

During last perioud I've seen more and more often the following situation.
Developer A creates a feature. Let's say is an autocomplete input. Let's assume for simplicity that no framework is used, html and js are on the same file. The file would look like this (let's also asume jquery used - is less to type):
<!-- autocomplete something.html file -->
.........................................
<input id="autocomplete" type="text" />
<script type="text/javascript">
$('#autocomplete').change(function() {
// Do lots of things here -> ajax request, parsing, etc.
});
</script>
.........................................
Now developer B sees the feature and says "Oh, nice feature, I can use that in my section, I'll put one at the top, and one at the bottom - because page is long".
And he does an ajax call to get that html file (this is important, I'm talking about features loaded like this, not features rewritten for the other section) and includes it where he needs it.
Now... problem. The first autocomple works, the second doesn't (because is select by id).
A workaround would be to modify, and use a class. And everything is ok, unless someone else (or himself, or whatever) uses same class for a tottaly different thing.
This could all be avoided if you could the the script to use as a "scope" (I know it is not the correct phrasing, couldn't find any better) the file where it was declared on.
Note: This is a theoretical question. For each particular case a solution could be found, but defining some kind of namespaces for this scenarios would solve the whole class of problems.
How could that be achieved?
As long as you're accessing the DOM the only way I see would be to use the "old" inline event:
<!-- autocomplete something.html file -->
.........................................
<input onchange="autocomplete_change();" type="text" />
<script type="text/javascript">
function autocomplete_change() {
// Do lots of things here -> ajax request, parsing, etc.
};
</script>
...........
Now the function name cannot be used by Developer B.
But when accessing DOM some kind of restriction will always be there.
However when creating the input by Script you can bind the handler directly to the Element:
<script>
var input = $('<input type="text"/>');
input.change(function() {
// Do lots of things here -> ajax request, parsing, etc.
});
document.body.appendChild(input[0]);
</script>

Page Specific JavaScript Function on Several Files of HTML

I have about 10 pages of HTML and each has a link to indexJS.js. I have a function loadMoreOnScroll() in the js file that is meant to run only for my index.html. But the loadMoreOnScroll() is run on all the pages as users scroll to the bottom.
How do I restrict loadMoreOnScroll() to only run for index.html?
Add classes to distinguish pages.
<body class="index">...
And with JavaScript:
if(document.body.className.match(/\bindex\b/)){
// code
}
of jQuery:
if($("body").hasClass("index")){
// code
}
Add a class to the body tag on the index then in javascript you can do something like
if(document.querySelector('body').className === 'myclass'){
loadMoreOnScroll();
}
Note: this assumes you have no other classes on the body. You could use a data attribute and do getAttribute('data-page') or something to similar effect.
You can just remove the loadMoreOnScroll() function from indexJS and create a new JavaSscript file with loadMoreOnScroll() in it. Be sure to include a reference to the new file in the index.html.
I'm assuming you're invoking loadMoreOnScroll from within your indexJS.js file, correct?
If so, the solution is to remove the function call from your javascript file and instead call it directly from index.html.
indexJS.js
// Create the function but don't call it here
function loadMoreOnScroll(){...}
index.html
<script src="indexJS.js></script>
<script>
// call the function
loadMoreOnScroll();
</script>
Edit:
A few other people suggested adding a body class and targeting your page that way. This approach is fine, and may work well in many scenarios but just keep in mind two things:
This works well for if you need to call your function on only one or two pages. Any more and you'll have to maintain a growing list of body classes within indexJS.js.
Using the body class as a hook decouples the function call from the page that its applies to.
In other words, the body class will have functionality tied to it that's not immediately obvious if you're only looking at the HTML. If you're working on the code yourself, you'll probably be ok, but in a team environment, it could be error-prone. (Or if you revisit the code after a few months). It all depends on the scope of your project.

jQuery call function when language in selectbox is changed

I have the following problem.
To translate a website, I'm using the jQuery Localize plugin.
This works fine. However, I want a CSS styled selectbox with flags and languages, and when a different option is selected the call to $("[data-localize]").localize("example", { language: $(this).attr('value') should be made to translate the page.
This code I'm currenly using, and it works fine for a plain, not-styled selectbox.
<script type="text/javascript">
$(function() {
$('#polyglot-language-options').change(function() {
if ($(this).attr('value') == "en") {
$("[data-localize]").localize("example", {
language: $(this).attr('value')
});
}
else if ($(this).attr('value') == "nl") {
location.reload();
}
});
});
</script>
But I want to style it, so I tried to integrate the "polyglot" language switcher. However, the current code doesn't work.
How can I integrate the $("[data.localize]").localize(); function in this code:
$('#polyglotLanguageSwitcher').polyglotLanguageSwitcher({
effect: 'fade'
});
This plugin (source code) does not follow the guidelines for jQuery plugin design. The bugs I found quickly:
It does not allow chaining, because it does not return this
It works only on one element at a time (does not use each())
It has a queer element hierarchy. It seems to require an element with an id, containing a form containing a select (as in the demo). In my opinion, such a plugin should be called on the language select element only.
It seems to navigate automatically, wanting to be configured with the page structure. Each of the li items in that fancy box contains a link to the respective page.
Therefore, it does neither trigger the form it live in or fire the change event you want to listen to.
As it stands, you can't use this particular plugin as you want to. If you want to fix all the bugs, I wish you a happy time :-) Nonetheless it might be possible to manipulate the plugin code, to let you register callbacks on select events (where you can invoke the localisation plugin). Otherwise, you will need to choose an other select plugin (or build one yourself from scratch, adapting the existing code)

web2py: How do I add javascript to Web2py Crud form?

Working with Web2Py. I'm trying to attach some javascript either to a field (onchange) or to the form (onsubmit), but I see absolutely no way to pass such argument to crud.create or to form.custom.widget.
Anyone has an idea?
Of course there is a way. The appropriate way is to ask people on the web2py mailing list who know how to, as opposed to generic stack overflow users who will guess an incorrect answer. :-)
Anyway, assume you have:
db.define_table('image',
Field('name'),
Field('file', 'upload'))
You can do
def upload_image():
form=crud.create(db.image)
form.element(name='file')['_onchange']='... your js here ...'
form.element('form')['_onsubmit']='... your js here ...'
return dict(form=form)
Element takes the css3/jQuery syntax (but it is evaluated in python).
I do not believe there is a way to do this directly. One option is just to manipulate web2py generated HTML, it is just a string. Even cleaner, in my opinion, is just to bind the event using jQuery's $(document).ready() function.
Say you have a database table (all is stolen from web2py's docs):
db.define_table('image',
Field('name'),
Field('file', 'upload'))
With form:
def upload_image():
return dict(form=crud.create(db.image))
Embedded in a view (in the simplest manner):
{{=form}}
And you want to add an onblur handler to the name input field (added to the view):
<script type="text/javascript">
$(document).ready(function(){
$("#image_name").blur(function(){
// do something with image name loses focus...
});
});
</script>

Where to put custom JavaScript/Prototype code for an ActiveScaffold form?

I have a Select dropdown on the form of an ActiveScaffold. I am trying to hide some of the fields on the form if a particular value is selected.
A [similar question][1] was posted to the ActiveScaffold Google Group, and the supplied Prototype code looks to do what I need, however I don't know where I need to add this.
--
I tried taking a copy of -horizontal-subform-header.html.erb from Vendor/plugins/
active_scaffold/frontends/default/views, placing it in views folder of my controller, and then adding my script into it:
<script type="text/javascript">
document.observe('dom:loaded', function() { //do it once everything's loaded
//grab all the product-input classes and call 'observe' on them :
$$('.product-input').invoke('observe', 'change', function(e) {
this.up('td').next('td').down('input').hide();
});
});
</script>
... but that doesn't seem to work properly. It works if I use a URL to go direct to the form (i.e. http://localhost:3000/sales/20/edit?_method=get). But when I test it with the main list view (i.e. http://localhost:3000/sales/) and opening the form via Ajax, then it doesn't work. Looking at the HTML source the just does not appear.
The common place for adding JavaScript is application.js found in public/javascripts. I'm a jQuery guy myself, however I'm sure you can hook up to the onchange event in application.js with prototype. A quick search looks like Event.observe should do the trick.

Categories

Resources