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.
Related
I know I've seen a beautifully straightforward answer to a similar question before, but I haven't been able to remember or locate it, so apologies in advance.
I'm not new to coding, but I've had no formal training with Javascript/jQuery. Everything else I used has been strictly typed, so I'm still struggling with how JS does typing. I have a function that fires every time a child of a specific class is changed (I'm writing this for Sharepoint, so there is some working-around that has to be done.)
Why is it when I write this:
$(".listen *").change(function(event) {
var element = event.target;
if (element.title == 'Workstation')) {
alert(element.val());
}
}
I get an error that .val() is not a function, and I have to instead write
$(".listen *").change(function(event) {
var element = event.target;
if (element.title == 'Workstation')) {
alert($('#' + element.id).val());
}
}
What is the difference between the object that "element" is and the object retrieved by using the id? Aren't they both jQuery objects? I realize that not all objects returned by my function might actually have a value to return, but I don't understand how the distinction is being made.
Thanks!
In your first code block the 'element' variable is not a jQuery object, it is a DOM object. The .val() method is not defined for DOM objects. It is only defined for jQuery objects.
In your second code block $('#' .element.id) returns a jQuery object that does have the val() method defined.
So to answer your question, No they are not both jQuery objects, only the second one is.
You must make jQuery object from your dom (event.target) like that;
$(".listen *").change(function(event) {
var element = $(event.target);
if (element.attr('title') == 'Workstation')) {
alert(element.val());
}
}
Then you can use your jQuery object as you want. By the way, if you want to catch the changed element, you can use $(this) instead of $(event.target).
$(".listen *").change(function(event) {
var element = $(this);
if (element.attr('title') == 'Workstation')) {
alert(element.val());
}
}
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 have a general question as the title states. I am just not sure if what I am doing is the proper way or perhaps there is an easier way to keep the scope of an object.
I have here an example just to illustrate what I am working with. You can see I am passing the "this" object to the function via a parameter however is this the only way to do it? Is there a way I don't need to pass the parameter.
Please note that the criteria is there is no identifying class/ids on the forms.
http://jsfiddle.net/TmaHs/
Thanks.
As per your example, there are easier ways :
$("form select").on('change', changeText);
function changeText(e) { //the event is still available
$("input", e.target.form).val("Changed!"); //so is the target and the form
}
will do the same. FIDDLE
As for more complex functions, passing an element to the function is usually not a problem, and in my opinion it's better than using globals.
You can use the standard bind function to explicitly set the context
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Or the similar equivalent in jquery if it has one (I'm not sure about this)
That's a valid way of doing it - an alternative is to declare the variable in a higher scope, assign it in the first function, and reference it without the need to use arguments in the second function.
See this edit to your JSFiddle for example.
You could work with the window object, by declaring the variable out of score, then setting the window-object of it.
$("form select").change(function() {
window.thisObj = $(this).parent("form");
changeText();
});
function changeText() {
window.thisObj.find("input").val("Changed!");
}
But the way you did it is the proper one, working with a window-object should always be avoided.
I don't see anything wrong in that approach... Its perfectly fine :)
BUT, if you still want to know the other way, then there are many...
I will post couple of them over here...
1) Retrieve all the values that you need in your selector function, and pass them to the function.
$("form select").change(function() {
var val1 = $("selector1").val();
var val1 = $("selector2").val();
var val1 = $("selector3").val();
changeText(val1,val2,val3);
});
function changeText(val1,val2,val3) {
// your operation
}
2) Add the required values to an array, and pass that array to your target function.
var targetArray = new Array();
$("form select").change(function() {
targetArray[0]=//your val1
targetArray[1]=//your val2
targetArray[2]=//your val3
changeText(targetArray);
});
function changeText(arr) {
// your operation
}
Here's what I'm aiming to achieve:
HTML
<fieldset id="addmore">
<p>blah</p>
<a class="remove">remove me</a>
</fieldset>
<a class="add">add more fieldsets</a>
Javascript
var addmore = new AddMore($('fieldset'));
addmore.buildCache(/*this will pull the innerHTML of the fieldset*/);
// bind the buttons
addmore.bind('add', $('a.add'));
addmore.bind('remove', $('a.remove'));
I've found myself having a lot more 'addmore' stuff in my HTML lately so I've been trying to build a class that will do all the leg work for me that I can just reuse in all my projects. The above code will, hopefully, be all I have to add each time and then the rest is done for me.
I've been winging this thing so, off the top of my head, here's what the class has to do:
Apply the jQuery bindings to the supplied 'button' objects so we can add/remove fieldsets
When a new fieldset is added, we have to recall the bind function so the new fieldset's 'a.add' button will work (I've found jQuery's .live() function to be buggy, for whatever reason, and try to avoid it)
It will hopefully do this with no memory leaks :}
Javascript Class
/*
Class to handle adding more data to the form array
Initialise the class by passing in the elements you want to add more of
Then bind 'add' and 'remove' buttons to the functions and the class will do the rest
*/
/*
Pass the jQuery object you want to 'addmore' of
Ex: var x = new AddMore($('fieldset.addmore'));
*/
function AddMore($element)
{
if (!$element || typeof($element) != 'object')
throw 'Constructor requires a jQuery object';
this.element = $element; // this is a jQuery object
this.cache = null;
}
/*
Supply clean HTML to this function and it will be cached
since the cached data will be used when 'adding more', you'll want the inputs to be emptied,
selects to have their first option selected and any other data removed you don't want readded to the page
*/
AddMore.prototype.buildCache = function(fieldset)
{
if (!fieldset)
throw 'No data supplied to cache';
this.cache = fieldset;
}
/*
use this to create the initial bindings rather than jQuery
the reason? I find .live() to be buggy. it doesn't always work. this usually means having to use a standard .bind()
and then re-bind when we add in the new set
that's what this class helps with. when it adds in the new data, it rebinds for you. nice and easy.
*/
AddMore.prototype.bind = function(type, $button)
{
if (!type || !$button && (type != 'add' && type != 'remove'))
throw 'Invalid paramaters';
// don't reapply the bindings to old elements...
if ($button.hasClass('addmore-binded'))
return;
// jQuery overwrites 'this' within it's scope
var _this = this;
if (type == 'add')
{
$button.bind('click', function()
{
_this.element.after(_this.cache);
});
}
}
I was going to have the .bind() method (in my class) call itself upon adding the new fieldset to reapply the binding but lost confidence with efficiency (speed/memory).
How should I tackle this? Do you have any pointers? Can you recommend improvements?
Thanks for the help.
In the most simplest form, you can do something like this:
var html = '{put html to add each time here}';
$('.add').click(function() {
$(html).insertAfter($('fieldset').last());
return false;
});
$('.remove').live('click', function() {
$(this).parent().remove();
return false;
});
You may need to tweak it based on your exact needs, but this should accomplish what you described in your example.
Update: sorry, remove should use the live method.
For creation of the new DOM elements, allow the specification/parameters to be any of the following:
simple HTML as a string (like the example above),
a function returning either a DOM element or HTML text. You can skip bind() or live() issues by adding
the onclick element when creating the HTML/element in the function. Although doing it in the AddMore() scope would be more tedious
if it's not a DOM element that gets returned.
inputs to a helper/factory method (maybe a template and name/value pairs) - postpone this unless you know enough patterns already.
Option #1 seems almost useless, but #3 might be hard unless you have extra time now.
Notes:
You might want to use $(theNewDomElement).insertBefore(_this.button); rather than _this.element.after(theNewDomElement); so that new items are append to the end of the list.
Actually, for insertBefore() you might just use this rather than _this.button since presumably the button (or anchor) is this, but then that limits the functionality - just make it some sort of DOM element (or a jQuery object that equates to one).
In most cases, I'd presume your DOM elements represent data that you'll want to save/send/transmit to your server, so provide a before-removal function, too. Even allow the function to skip removal -- like after a confirm().
Good luck.
jQuery is currently providing me with a fun introduction to Javascript after 12 years of surviving happily without. I'm at the stage where I'm trying to learn as much as I can about optimising the code I write and, whilst I have found plenty of good reference material, there is something quite basic which is puzzling me and I have been unable to find anything about it anywhere.
When I'm attaching something to an element how should I be referring to that element within the function. For example, when attaching a function to an element's click event :
$('#a_button',$('#a_list_of_buttons')).click(function() {
// NOW WHAT'S THE BEST WAY TO REFER TO '#a_button' ?
});
I know not to keep re-selecting it like so as the browser has to search the whole DOM again from scratch to find what it's already found once :
$('#a_button').click(function() {
// I KNOW THAT THIS IS NAUGHTY
var buttonValue = $('#a_button').val();
$('#a_button').addClass('button_has_been_clicked');
});
Currently I'm using either of the following but am not entirely sure what each is actually doing :
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
But is this just re-selecting like in the first "naughty" example?
$('#a_button').click(function(event) {
// USING event.target
var buttonValue = $(event.target).val();
$(event.target).addClass('button_has_been_clicked');
});
This seems like it might be better but is it efficient to refer to 'event.target' multiple times?
$('#a_button').click(function(event) {
// USING A LOCAL VARIABLE
var thisButton = $(this);
// OR SHOULD THAT BE
var thisButton = $(event.target);
var buttonValue = thisButton.val();
thisButton.addClass('button_has_been_clicked');
});
I understand the performance efficiencies of passing things to variables but I'm unsure whether or not in these situations using $(this) or $(event.target) provides me with the same efficiencies already and so by setting a new variable I'm actually doing more work that I need to.
Thank you.
this and event.target are not always the same.
this refers to the element you assigned the listener to ( in this case the '#a_button' ). event.target however is the element that actualy triggered the event, which can be a childnode of #a_button.
So $(this) is the thing you are looking for.
See reference: http://api.jquery.com/event.target/
I may be wrong, but this and event.target are both just different references to the same element.
this and event.target are not always references to the same element. But in answer to your question, var thisButton = $(this); is definitely the winner. If you were writing C# code, you would never do the following:
this.Controls[0].Controls[0].Text = "Foo";
this.Controls[0].Controls[0].Controls.Clear();
You would do this:
var control = this.Controls[0].Controls[0];
So you probably should never re-use $(this) more than once either. Althought it's trivial to convert this from a DOM element to a jQuery object, it's still an unnecessary overhead.
However, sometimes you need to gear back from optimisation to make sure your code maintains it's readability.
Another option of course is just to change what this is. This is javascript afteral:
this = $(this); // Now `this` is your jQuery object
Disclaimer: I only just tried the above and it seemed to work. Might have some issues though.
I built a little example to demonstrate how this and e.target actually work: http://jsfiddle.net/xZAVa/
In my experience i would go with the following:
$('#a_button').click(function() {
// USING this
var buttonValue = $(this).val();
$(this).addClass('button_has_been_clicked');
});
The this in the context of your click callback method is a reference to the DOM event. Since you already have a reference to the DOM object it is trival to convert it into a jQuery object since a lookup is not required.
But on a side note, if you don't need to use jQuery in your callback, then don't. You can simply get the value of the button using standard JS this.currentTarget.value.
The other examples you mentioned require a DOM lookup, and depending on the complexity of your selector can take longer. Using a id based lookup like '#a_button' will perform better than a class based looked like .myClass.