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.
Related
http://jsfiddle.net/PhilFromHeck/KzSxT/
In this fiddle, you can see at line 38 in the Javascript that I've attempted to make a comparison that isn't working. I believe it because one of the variables is an Object, where the other is an Element; does anyone have any advice as to how I can can find a match between these two?
menuID[0] = document.getElementById('menuOne');
menuID[1] = document.getElementById('menuTwo');
menuID[2] = document.getElementById('menuThree');
menuID[3] = document.getElementById('menuFour');
$('.menu').mouseenter(function () {
for (var i = 0; i < 3; i++) {
if(menuID[i] == $(this)){
//this condition is not met, there's an alert which will add more detail in the fiddle
}
}
}
Method document.getElementById returns a DOM element an not a jQuery object. In the mouseenter event handler this refers to a DOM element as well.
So in order to compare them you shouldn't convert this to a jQuery object:
if (menuID[i] === this) { ... }
You want to use jQuery's .is() for this.
if($(this).is(menuID[i])){
A few issues I see here:
One is simply that, in your jsfiddle, the first 4 lines of code that you list aren't running before the bottom block runs. I'm not sure why you have both an init function that you attach to window.onload and a document.ready() function; but you'll want to make sure that init runs.
Secondly; as VisioN said, I think the main issue is that you're trying to compare a jQuery wrapper around a DOM element $(this) with a DOM element (the result of getElementById). As he says, this == menuID[i] will work.
At a design level, why not simply use the id to identify the element? this.id will give you the the id; why not simply use that to determine which menu div you're looking at?
With jquery, I've got the following code:
$('a[data-hello]').click(function(){ = That select all "a" elements with "data-hello".
I'm trying to make this with raw Javascript. I stop here:
document.querySelectorAll("data-hello").onclick = function() {
(btw, theres a way to select all the A elements with data-hello and not all with data-hello? o.O)
But querySelectorAll returns a Array. Because of this, it only works if I determine a position. This way:
document.querySelectorAll("data-hello")[5].onclick = function() {
But I want ALL ELEMENTS, not specific elements, like with jQuery. I cant use jQuery.
It is so simple with Jquery :( I must make a "for" to wade through all the positions in JS? Is this necessary? sorry I do not understand...
What I want to do:
I want to get the data attribute value of the element that is clicked. I use this for this inside the function and, then, I applied another function that add a class in a specific element.
Basically, there is buttons with classes in data attribute value. This classes will be applied to a specific element.
Put the array (actually a NodeList) of elements in a variable and loop through them to set the event handler on each of them. That's what the jQuery methods do to apply something to all elements in a jQuery object. There is no way around the loop, with jQuery it's just hidden within the methods. You can use the same selector syntax as in jQuery with querySelectorAll.
var arr = document.querySelectorAll("a[data-hello]");
var f = function() {
// do something
};
for (var i = 0; i < arr.length; i++) {
arr[i].onclick = f;
}
querySelectorAll accepts a string of comma-separated CSS selectors, just like jQuery, so you can give it the same string: 'a[data-hello]'.
The difference between native and jQuery that you are running into is in calling methods on the elements returned. jQuery returns a jQuery object, which has methods that often loop over all the elements, .click() being one such methods. You need to replicate that with the array of elements that querySelectorAll is returning by looping over the array and applying the same handler to each element's onclick property.
Try this:
var myElements = document.querySelectorAll("a[data-hello]");
Array.prototype.forEach.call(myElements, function (element) {
element.onclick = function () {
// Your onclick handler code goes here.
console.log('clicked', element);
};
});
as simple as that:
var dataElems = document.querySelectorAll("[data-hello]")
for (var i=0;i<dataElems.length;i++) {
dataElems[i].onclick = function(i,v) {
alert(this.innerHTML)
}
}
example http://jsfiddle.net/acrashik/W86k8/
I was trying to use $.fn.show (and other jQuery functions) within higher-order functions.
What I originally wanted to have was a function that applies a given function to all elements returned by a collection of other functions applied to a given element. Something that would look like this:
function mapOn( func, genratingFunc, element ){
$(generatingFuncs).each(function(){
var buf = $(element);
while(buf.length){ // run as long as elements are returned
func(buf);
buf = this(buf);
}
});
}
I needed such a function to apply some functions to a couple of DOM nodes and their parents and/or children in a handy, expressive way. Let's say we want to hide the node with the ID hideMyFamily and its children. I don't know any handy way to do this with jQuery so I'd run hide() on $("#hideMyFamily").children() and on $("#hideMyFamily").children().children() and so on until the length of the collection was 0 (and on $("#hideMyFamily") itself of course).
Thing is, running mapOn( $.fn.show, [$.fn.children], $("#hideMyFamily") ) won't do the job since you apparently cannot just apply $.fn.show to an element/collection.
So what I came up with is this:
For each of the jQuery's functions that I need to specify another function (within global scope) that looks like this:
function _show(e){ $(e).show(); }
For each of the jQuery's "generating" functions I specify another "work-around function":
function _id(e){ return $(e); }
function _children(e){ return $(e).children(); }
And then I can specify my "multiMap" function which looks like this:
function multiMap(func, generators, elem){
$(generators).each(function(){
var buf = $(elem);
var buf2 = [];
while (buf.length && buf[0] !== buf2[0]) {
func(buf);
buf2 = buf;
buf = this(buf);
}
});
}
Now I can run my handy function multiMap(_show, [_id, _children], "hideMyFamily") to hide the element itself and all of its children.
Now, to get to the point, my question is: Is there any more elegant way to achieve the desired behaviour? Is there any jQuery magic I didn't take into account?
tl;dr Is there a handy way to use jQuery's functions like show() and hide() on nodes/collections in a way like $.fn.show( $("someElements") )?
Yes, it is possible.
You can do $.fn.show.call($("some-elements")).
I haven't fully gone through your more elaborate example, but that seems to be what you're looking for. And for your final example, you could write things as:
multiMap($.fn.show, [_id, _children], "hideMyFamily")
and then in mutliMap do f.call or something like that and I believe that would work.
I'm generating an unordered list through javascript (using jQuery). Each listitem must receive its own event listener for the 'click'-event. However, I'm having trouble getting the right callback attached to the right item. A (stripped) code sample might clear things up a bit:
for(class_id in classes) {
callback = function() { this.selectClass(class_id) };
li_item = jQuery('<li></li>')
.click(callback);
}
Actually, more is going on in this iteration, but I didn't think it was very relevant to the question. In any case, what's happening is that the callback function seems to be referenced rather than stored (& copied). End result? When a user clicks any of the list items, it will always execute the action for the last class_id in the classes array, as it uses the function stored in callback at that specific point.
I found dirty workarounds (such as parsing the href attribute in an enclosed a element), but I was wondering whether there is a way to achieve my goals in a 'clean' way. If my approach is horrifying, please say so, as long as you tell me why :-) Thanks!
This is a classic "you need a closure" problem. Here's how it usually plays out.
Iterate over some values
Define/assign a function in that iteration that uses iterated variables
You learn that every function uses only values from the last iteration.
WTF?
Again, when you see this pattern, it should immediately make you think "closure"
Extending your example, here's how you'd put in a closure
for ( class_id in classes )
{
callback = function( cid )
{
return function()
{
$(this).selectClass( cid );
}
}( class_id );
li_item = jQuery('<li></li>').click(callback);
}
However, in this specific instance of jQuery, you shouldn't need a closure - but I have to ask about the nature of your variable classes - is that an object? Because you iterate over with a for-in loop, which suggest object. And for me it begs the question, why aren't you storing this in an array? Because if you were, your code could just be this.
jQuery('<li></li>').click(function()
{
$(this).addClass( classes.join( ' ' ) );
});
Your code:
for(class_id in classes) {
callback = function() { this.selectClass(class_id) };
li_item = jQuery('<li></li>')
.click(callback);
}
This is mostly ok, just one problem. The variable callback is global; so every time you loop, you are overwriting it. Put the var keyword in front of it to scope it locally and you should be fine.
EDIT for comments: It might not be global as you say, but it's outside the scope of the for-loop. So the variable is the same reference each time round the loop. Putting var in the loop scopes it to the loop, making a new reference each time.
This is a better cleaner way of doing what you want.
Add the class_id info onto the element using .data().
Then use .live() to add a click handler to all the new elements, this avoids having x * click functions.
for(class_id in classes) {
li_item = jQuery('<li></li>').data('class_id', class_id).addClass('someClass');
}
//setup click handler on new li's
$('li.someClass').live('click', myFunction )
function myFunction(){
//get class_id
var classId = $(this).data('class_id');
//do something
}
My javascript fu is pretty weak but as I understand it closures reference local variables on the stack (and that stack frame is passed around with the function, again, very sketchy). Your example indeed doesn't work because each function keeps a reference to the same variable. Try instead creating a different function that creates the closure i.e.:
function createClosure(class_id) {
callback = function() { this.selectClass(class_id) };
return callback;
}
and then:
for(class_id in classes) {
callback = createClosure(class_id);
li_item = jQuery('<li></li>').click(callback);
}
It's a bit of a kludge of course, there's probably better ways.
why can't you generate them all and then call something like
$(".li_class").click(function(){ this.whatever() };
EDIT:
If you need to add more classes, just create a string in your loop with all the class names and use that as your selector.
$(".li_class1, .li_class2, etc").click(function(){ this.whatever() };
Or you can attach the class_id to the .data() of those list items.
$("<li />").data("class_id", class_id).click(function(){
alert("This item has class_id "+$(this).data("class_id"));
});
Be careful, though: You're creating the callback function anew for every $("<li />") call. I'm not sure about JavaScript implementation details, but this might be memory expensive.
Instead, you could do
function listItemCallback(){
alert("This item has class_id "+$(this).data("class_id"));
}
$("<li />").data("class_id", class_id).click(listItemCallback);
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.