I am using jQuery events to capture events across a rails app. Basically, there are a set of event captures' on DOM elements that then call other functions. What I'd like to do is provide some namespacing to these event captures and an looking for the best way:
I currently have (but like 60 of them):
$(document).ready(function(){
$('.edit-item').on('click', arc.event_handler.edit_item);
});
and would like something like the following - basically provide the edit_item so we know where to look:
$(document).ready(function(){
var events.edit_item= {
$('.edit-item').on('click', arc.event_handler.edit_item);
};
});
But this is giving me an error. I am familiar with basic object literal syntax like:
var my = {
say_my_name: function(){
alert('my name');
}
}
but not sure how to apply it with jQuery functions. How would I do this?
I am aware that there are anonymous functions for namespacing this more agressively but, honestly, just want this one change right now
thx in advance
You seem to want
var events = {
"edit_item": $('.edit-item').on('click', arc.event_handler.edit_item)
};
or
var events = {};
events.edit_item = …;
// equal to
events["edit_item"] = …; // Here you could use any expression (like a variable)
// instead of the string literal
Now events.edit_item is the jQuery object returned by the expression.
Perhaps this is useful:
var events;
$(document).ready(function(){
events = {
edit_item: $('.edit-item').on('click', arc.event_handler.edit_item),
other_item: $('.other-item').on(/* something else */),
//...
// the last item without trailing comma
};
});
Please note the commas at the end of the lines. IE however dislikes the comma after the last line, so omit it.
The events object contains the jQuery objects, so you can bind more events to it or do other jQuery operations on them.
Related
I'm creating a simple jQuery plugin and am having trouble making it modular.
For example:
$.fn.testMethod = function(option1, option2) {
var something1 = option1;
var something2 = option2;
};
My problem starts to occur when I have more than one element using this method. For example:
$('.element1').testMethod(1, 2);
$('.element2').testMethod(3, 4);
The second element ends up using the variable of the first later down the line if I'm changing stuff around. What would be a better way of doing this and locking the variables to the specific element they're being used with? If this is too vague, I can paste in my full code but it is a bit complex.
When working with jQuery plugins, you generally want to do an each loop and return the collection.
That also means you can't use just simple variables, but that's where jQuery's data comes in handy.
$.fn.testMethod = function(option1, option2) {
return this.each(function() {
$(this).data('something1', option1);
$(this).data('something2', option2);
// do stuff
var something = $(this).data('something1'); // etc
});
};
On the other hand, the arguments and variables inside the function will be unique to each function call, so there's no way the second call to the function would use the variables from the first call unless you're doing something else strange.
Fiddle (Uses JQuery) - http://jsbin.com/ponikasa/1/edit
I know JQuery is Javascript, but for the sake of an argument how do you write the following in pure Javascript without the need for a js library like JQuery?
$(document).ready(function() {
$('.preview-site').on('click', function(){
window.open('javascript:document.write("'+ $('.workflow').val() +'")', 'Opened Page', 'width=660, height=440');
return false;
});
});
I tried this, but doesn't work.
Any help is greatly appreciated.
window.onload = function() {
var preview = document.getElementsByClassName("preview-site"),
code = document.getElementsByClassName("workflow")[0].value;
preview.onClick = function() {
window.open('javascript:document.write("'+ code = +'")', 'Opened Page', 'width=660, height=440');
return false;
}
}
Well to write in javascript you would do the following
document.addEventListener('DOMContentLoaded', function() {
var previewSite = this.querySelectorAll('.preview-site');
var handler = function() {
var workflow = document.querySelector('.workflow')
window.open('javascript: document.write(' + workflow.value + ')', 'Opened Page', 'width=660, height=440')
return false;
};
for( var i = 0; i < previewSite.length; i++) {
previewSite[i].addEventListener('click', handler);
}
});
The problem you had is getElementsByClassName returns a collection, so you cannot use value or onclick on the collection.
I use querySelectorAll because it's easier and has almost better support that getElementsByClassName
I don't usually answer questions like this, but I am highly supportive of anyone that uses jQuery that want's to actually learn javascript it's self
also, in your question, you have onClick, for the event handler you want onclick
For one minor performance improvement you could move workflow out of handler, that way it won't fetch it on every click, only do this if you don't intend to add dynamic .workflow
Yeah, and also. (as pointed out in comments) window.onload is not the same as document ready, window.onload will wait for images & media to be fully loaded, so use DOMContentLoaded
One of the things jQuery selectors do is try to abstract the "array" when calling functions and assigning handlers. Consider something like this:
$('.preview-site').on('click', function(){
// code
});
This code doesn't just assign the click handler. On a lower level than that presented by the jQuery interface, this iterates the array of .preview-site elements and assigns the click handlers to each element. Sometimes it's one element, sometimes it's many. (Sometimes it's none.) jQuery makes the interface the same regardless of the count.
Without it, you need to handle that difference explicitly. These values are arrays:
var preview = document.getElementsByClassName("preview-site"),
code = document.getElementsByClassName("workflow");
Even if each one only finds a single element by that class name, the result from document.getElementsByClassName() is an array. So even if the array has only one element, it's still an array. And you can't assign a handler to an array, you need to assign it to each element in the array. Potentially something like this:
for (var i = 0; i < preview.length; i++) {
preview[i].addEventListener('click', function() {
window.open('javascript:document.write("'+ code[i].value[0] +'")', 'Opened Page', 'width=660, height=440');
return false;
}
}
Naturally, you'd probably want to put in some checks to ensure that the two arrays are the same length before assuming that for each preview element there exists a code element. But the principle is the same. You just need to account for the enumeration of the array manually.
I am using the following function closure in a jqgrid (a jquery grid) to retain changes in edits when paging in a variable called 'retainedChanges'- does this look ok; Im i breaking any good practices in javascript;
the code works alright just want to make sure I dont introduce features that can break in the future
(function($){
var retainedChanges;
retainedChanges = new Array();
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);
This works fine, although you should probably accept jQuery as an argument:
(function($){
This way, even if the $ symbol is being used for something else outside of your closure, it won't effect your code inside the closure.
2 more things:
1) You should declare and assign you variable together, and use [] instead of new Array().
2) You're missing a $ symbol here: ('#grid').
For a full rundown, look at this:
(function($){
var retainedChanges = [];
$.retainChangesOnPaging = function(){
var changedCells = $('#grid').jqGrid('getChangedCells');
// loop over changedCells array, removing duplicates if you want to...
return retainedChanges.push(/* this is inside the loop; push current value to array*/);
....
}
$.getRetainedChanges = function(){
return retainedChanges;
}
})(jQuery);
You are passing jQuery into a function that has no arguments and never uses the jQuery object passed in. You may have meant:
(function($){
Other than that it looks fine.
There are several things you could improve:
1) You pass jQuery to the function, but do not use it (you use global object $, if it is defined). Modify your code to accept one parameter, named $:
(function($){
2) You can shorten retainedChanges declaration:
var retainedChanges = new Array();
3) If you are trying to write jQuery plugin, then follow the following tutorial: jQuery: Plugins/Authoring
If not, then maybe use different global object than jQuery?
I am just wondering what the pros/cons would be between these two styles of coding:
var foo;
$(document).ready( function() {
foo = $find(fooID);
});
function OnBarClicked()
{
foo.doStuff();
foo.doMoreStuff();
}
compared to
function OnBarClicked()
{
$find(fooID).doStuff();
$find(fooID).doMoreStuff();
}
I feel like there's probably a gotcha in the former, but I'm not aware of why there would be a gotcha. In addition, if there's any seek time for $find().. is it more efficient to find all variables guaranteed to be 'found' 2+ times at the start, and then use the one instance?
EDIT: $find description and usage
var radListBox1ID = "<%= RadListBox1.ClientID %>";
var radListBox = $find(radListBox1ID);
alert(radListBox.get_id()); //Alerts RadListBox1.ClientID
Assuming you're asking if querying the DOM once vs multiple (one per statement) with $()...
Yes, it is more efficient to use a single $() and store the result in a variable, rather than invoke jQuery at every statement if you are using the element multiple times within the same scope...
This is particularly useful in event handlers where you are referencing this as a jQuery object multiple times.
$('#element').click(function(e) {
var $clicked = $(this);
$clicked.doStuff();
$clicked.doMoreStuff();
});
First of all, you felt that there was a gotcha in the first example, and indeed, there is: You declare the foo variable in a local context that your onBarClicked function doesn't have access to. You'll get an undefined error if you try to use it. To fix that problem, declare foo in the global scope:
var foo;
$(document).ready(function () {
foo = $(fooID);
});
When it comes to your actual question, the short answer is: Yes. Running the search once and storing the result will be faster.
But if you're only searching for an element by its ID, then in all honesty it probably won't make much of a difference. The times when it really matters are when you have a complicated selector that involves attribute selectors or pseudo-elements or descendant combinators or stuff. You'd definitely want to save that result instead of running it again and again and again.
The first one doesn't work as your would expect since foo won't be defined in OnBarClicked. foo gets set in the callback function once the document is ready.
I recommend this aproach:
$( function () {
// get all the references (to the DOM elements) ASAP
var header = $( '#header' )[0],
nav = $( '#navigation' )[0],
toolbar = $( '#toolbar' )[0];
// etc.
// now whenever you need to work with a referenced DOM element
// just wrap it inside a jQuery object
$( nav ).click( function () { ... });
});
I've created a JavaScript object to hold onto a value set by a user checking a checbox in a ColorBox.
I am relatively new to jQuery and programming JavaScript "the right way" and wanted to be sure that the below mechanism for capturing the users check action was a best practice for JavaScript in general. Further, since I am employing jQuery is there a simpler method to hold onto their action that I should be utilizing?
function Check() {
this.Checked = false;
}
obj = new Check;
$(document).ready(function() {
$('.cboxelement').colorbox({ html: '<input id="inactivate" type="checkbox" name="inactivatemachine"> <label for="inactivate">Inactivate Machine</label>' });
$(document).bind('cbox_cleanup', function() {
obj.Checked = $.fn.colorbox.getContent().children('#inactivate').is(':checked');
});
$(document).bind('cbox_closed', function() {
if ($($.fn.colorbox.element()).attr('id').match('Remove') && obj.Checked) {
var row = $($.fn.colorbox.element()).parents('tr');
row.fadeOut(1000, function() {
row.remove();
});
}
});
});
Personally, I would attach the value(s) to an object directly using jQuery's built-in data() method. I'm not really entirely sure what you are trying to do but, you can, for instance, attach values to a "namespace" in the DOM for use later one.
$('body').data('colorbox.checked',true);
Then you would retrieve the value later by:
var isChecked = $('body').data('colorbox.checked');
You run the data() method on any jquery object. I would say this is best-practice as far as jQuery goes.
You could capture the reference in a closure, which avoids global data and makes it easier to have multiple Checks. However, in this case it appears to be binding to the single colorbox, so I don't know that you could usefully have multiple instances.
function Check() {
this.Checked = false;
var obj = this; // 'this' doesn't get preserved in closures
$(document).ready(function() {
... as before
)};
}
var check = new Check; // Still need to store a reference somewhere.
$($.fn.colorbox.element()) is redundant. $.fn.colorbox.element() is already a jquery element.
It's common use (in the examples i watched, at least) to prepend a $ to variables referencing jquery elements.
So, var $rows = $.fn.colorbox.element().parents('tr'); gives instantly the idea that it is referencing jquery element(s).
I am afraid fadeOut won't work on rows in IE6 (if i recall correctly). You should be able to hide all the content inside the <tr> before removing it.
Can't help on the "simplify" thing because i don't know the colorbox's best uses.