jQuery each() doesn't work using OOP approach - javascript

This is my situation in javascript:
EA = {
DOMElement: $('#example img'),
photos: '',
...
countSelected: function(){
var len = 0;
var id = '';
EA.DOMElement.each(function(index){
id = $(this).attr('data-id');
EA.photos += id + ",";
});
len = EA.photos.length;
return len;
},
}
The function countSelected return 0 but if I use this solution all works fine:
countSelected: function(){
var len = 0;
var id = '';
$('#example img').each(function(index){
id = $(this).attr('data-id');
EA.photos += id + ",";
});
len = EA.photos.length;
return len;
},
In my opinion the problem is in selector: $(this).
Do you know any solution?
Thanks!

The question is, when did you initialize the namespace? If you did so before the DOM was fully ready, and/or if images were added later under that #example element, then the jQuery reference is stale.
You have a couple of options. One is to initialize it later; another is to use the jQuery call every time (like in your second code snippet); another is to create a function in the namespace that will invoke the jQuery constructor whenever you need it (which at least allows you to reduce selector duplication).

Related

Simple count function does not work

I thought making a simple function where if you click on a button a number will show up inside of a paragraph. And if you continue to click on the button the number inside the paragraph tag will increase. However, I'm getting an error message saying that getElementsByTagName is not a function. Here is the code on jsfiddle, I know there is something simple that I'm doing wrong but I don't know what it is.
HTML
<div class="resist" id="ex1"><h2>Sleep</h2><p></p><button>Resist</button></div>
<div class="resist" id="ex2"><h2>Eat</h2><p></p><button>Resist</button></div>
Javascript
var count = 0;
var resist = document.getElementsByClassName('resist') ;
for(var i = 0; i < resist.length; i++)
{ var a = resist[i];
a.querySelector('button').addEventListener('click', function(a){
count +=1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
}
You are overwriting a variable with event object passed into event handler. Change the name to e maybe, or remove it altogether as you are not using it anyway:
a.querySelector('button').addEventListener('click', function(e /* <--- this guy */) {
count += 1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
Another problem you are going to have is classical closure-in-loop issue. One of the solutions would be to use Array.prototype.forEach instead of for loop:
var count = 0;
var resist = Array.prototype.slice.call(document.getElementsByClassName('resist'));
// ES6: var resist = Array.from(document.getElementsByClassName('resist'));
resist.forEach(function(a) {
a.querySelector('button').addEventListener('click', function(e) {
count += 1;
a.getElementsByTagName('p')[0].innerHTML = count;
});
});
vars in Javascript are function scoped, so you must wrap your event listener binding in a closure function to ensure the variable you're trying to update is correctly set.
(Note: I've renamed a to div in the outer function and removed the arg from the inner click function).
var count = 0;
var resist = document.getElementsByClassName('resist') ;
var div;
for(var i = 0; i < resist.length; i++)
{
div = resist[i];
(function(div){
div.querySelector('button').addEventListener('click', function(){
count +=1;
div.getElementsByTagName('p')[0].innerHTML = count;
});
})(div);
}

How do I build up my Javascript/Jquery for many elements using a loop?

instead of repeating the same code over and over again in my js file with the only difference being the element names, I was hoping to build a loop that would build out my js.
I'm tryign to add toggle functions to some buttons on my page that change their colors and sets a value elsewhere on my page. Here is my code:
var className;
var idName;
var i;
for (i = 0; i < 11; i++) {
className = ".feedbackq";
idName = "#feedbackq";
className = className + i.ToString();
idName = idName + i.ToString();
$(className).toggle(
function () {
$(className).each(function () {
$(className).css("background-color", "");
});
$(this).css("background-color", "red");
var value = $(this).val();
$(idName).val(value);
},
function () {
$(this).css("background-color", "");
$(idName).val("");
});
}
This is unfortunately not doing anything. When not in a loop, with hardcoded variable names, the code works, but I need this to be dynamic and constructed through a loop. The 11 count that is shown will eventually be a dynamic variable so I can't do hard coding....
Thanks for the help!
UPDATE: As requested, here is the not in the loop code:
$(".feedbackq0").toggle(
function () {
$(".feedbackq0").each(function () {
$(".feedbackq0").css("background-color", "");
});
$(this).css("background-color", "red");
var value = $(this).val();
$("#feedbackq0").val(value);
},
function () {
$(this).css("background-color", "");
$("#feedbackq0").val("");
});
$(".feedbackq1").toggle(
function () {
$(".feedbackq1").each(function () {
$(".feedbackq1").css("background-color", "");
});
$(this).css("background-color", "red");
var value = $(this).val();
$("#feedbackq1").val(value);
},
function () {
$(this).css("background-color", "");
$("#feedbackq1").val("");
});
$(".feedbackq2").toggle(
function () {
$(".feedbackq2").each(function () {
$(".feedbackq2").css("background-color", "");
});
$(this).css("background-color", "red");
var value = $(this).val();
$("#feedbackq2").val(value);
},
function () {
$(this).css("background-color", "");
$("#feedbackq2").val("");
});
One way to do this (without seeing your HTML for further simplifications) is to put the index number on the object before your event handlers using .data() so it can be retrieved later upon demand independent of the for loop index which will have run its course by then:
var className, idName, i;
for (i = 0; i < 11; i++) {
className = ".feedbackq" + i;
$(className).data("idval", i).toggle(
function () {
var idVal = $(this).data("idval");
$(".feedbckq" + idVal).css("background-color", "");
$(this).css("background-color", "red");
var value = $(this).val();
$("#feedbackq" + idVal).val(value);
},
function () {
var idVal = $(this).data("idval");
$(this).css("background-color", "");
$("#feedbackq" + idVal).val("");
});
}
Note: I've made a bunch of other simplifications too:
I declare multiple variables with one var statement.
toString(i) is not needed to add a number onto the end of a string and you had it mispelled too (with the wrong capitalization)
.each() is not needed to apply .css() to every item in a jQuery collection
I suspect that if we could see your HTML, we could significantly simplify this further as there are probably relationships between items that could be exploited to reduce code, but without the HTML we can't offer any advice on that.
You probably fell victim to the closures-inside-for-loops bug. You need the code inside the loop to be in a separate function, so each iteration gets its own className variables instead of them sharing the variables.
You could do this by crating a named function or by using a jQuery iterator function with a callback instead of a for loop
var toggle_stuff = function(i){
var className = ".feedbackq" + i; //The variables are local to just this iteration now
var idName = "#feedbackq" + i; //No need to call toString explicitly.
//And so on...
}
for(var i=0; i<11; i++){
toggle_stuff(i)
}
I kinda suspect that you are calling the wrong function, should be .toString() instead of .ToString().
Note that JavaScript is case-sensitive.
But if I write the code anyway I will ignore the .toString() part and use the numeric value directly...

Duplicating JQuery/Javascript functions

I have a jquery/javascript function that creates an array to be placed in a form's hidden field. However, this is a nested form and so I need to invoke this function many times to populate the hidden field for all the children: test_suite_run[test_runs_attributes][//id][packages_id]. This means that I need to run this function with a different child id each time.
I have added //id to indicate the only differences between the many function calls. I do not know how to duplicate this function without copying it many times manually and replacing //id with the indexes 0...n, for each nested child instance. Could this somehow be done by passing parameters to the javascript function?
Sorry if this a little confusing, I will be happy to explain in more detail if needed.
JQuery Function
$(document).ready(function () {
arr = new Array();
$(document).on('change', 'select[id ^="s_package//id"]', function () {
var arr = $('select[id ^="s_package//id"]').map(function () {
return this.value
})
result = ""
for (j = 0; j < arr.length - 1; j++) {
result += (arr[j] + ", ");
}
result += (arr[arr.length - 1])
$("input[name='test_suite_run[test_runs_attributes][//id][packages_id]']").val(result);
});
});
You can pass an array of ids to use in your function and iterate them:
function somethingMeaningful(ids) {
for (var i = 0, l = ids.length; i < l; i++) {
var id = ids[i];
// do something with this id
}
}
$(function() {
somethingMeaningful(['id1', 'id2', 'idn']);
});
It might also be possible to simplify your selector and calculate the id at runtime, depending on their actual format:
$(document).on('change', 'select[id^="s_package"]', function () {
var id = $(this).attr('id').slice('s_package'.length);
// Do stuff with real id
});

Jquery to Prototype

How do I convert the jquery code below to prototype?
<script>
$(document).ready (function() {
$("#valor").keyup(function(){
var resultVal = 0.0;
var objRegExp = '\s+';
$("#valor").each ( function() {
resultVal += parseFloat ( $j(this).val().replace(/\s/g,'').replace(',','.'));
});
$("#total").val(resultVal);
});
});
</script>
Thanks.
I don't know, but pure JavaScript is always nice:
function doLoad() {
var valor = document.getElementById("valor");
valor.onkeyup = function() {
var resultVal = 0.0;
var objRegExp = '\s+';
for(var i = 0; i < valor.childNodes.length; i++) {
var n = valor.childNodes[i];
if(n.nodeType === 1) resultVal += parseFloat(n.value.replace(/\s/g, '').replace(',', '.'));
}
document.getElementById("total").value = resultVal.toString();
};
}
if(window.addEventListener)
window.addEventListener("load", doLoad, false);
else
window.attachEvent("onload", doLoad);
Here's a working example with the migration:
http://jsfiddle.net/MaQA5/
Code:
eventObserver = function() {
var resultVal = 0.0;
var objRegExp = '\s+';
$$(".valor").each(function(el) {
resultVal += parseFloat($(el).getValue().replace(/\s/g, '').replace(',', '.'));
});
$("total").setValue(resultVal);
};
$$('.valor').each(function(el) {
el.observe('keyup', eventObserver);
});
Some comments:
From your code, I supposed you have several inputs with the same id (valor). If this is the case, that's wrong, as ids must be unique in the whole DOM.
That's why I changed that for a class named valor.
Prototype has a special $$ function to get elements by css-selector. But uses $ for id search or to turn a DOM element into a Prototype-empowered element.
When calling to each method, instead of using this as every element in the original collection (like you do in jQuery), you must use the first argument in the function: el.
Instead of calling the keyup jQuery method, you must use observe('keyup', ....
In my opinion, jQuery is more elegant, or at least, the Prototype that I know is not that fancy :)

Pure JavaScript equivalent of jQuery click()?

I am building a small app which captures mouse clicks. I wrote the prototype in jQuery but, since it is a small app focusing on speed, embedding jQuery to use just one function would be an overkill.
I tried to adapt this example from JavaScriptKit:
document.getElementById("alphanumeric").onkeypress=function(e){
//blah..blah..blah..
}
but it didn't work when I tried this:
document.getElementsByTagName("x").onclick
What am I doing wrong?
Say you have a list of p tags you would like to capture the click for the <p> tag:
var p = document.getElementsByTagName("p");
for (var i = 0; i < p.length; i++) {
p[i].onclick = function() {
alert("p is clicked and the id is " + this.id);
}
}
Check out an example here for more clarity:
http://jsbin.com/onaci/
In your example you are using getElementsByTagName() method, which returns you an array of DOM elements. You could iterate that array and assign the onclick handler to each element, for example:
var clickHandler = function() {
alert('clicked!');
}
var elements = document.getElementsByTagName('div'); // All divs
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = clickHandler;
}
it looks a little bit like you miss more than just the click function of jQuery. You also miss jquery's selector engine, chaining, and automatic iteration across collections of objects. With a bit more effort you can minimally reproduce some of those things as well.
var myClickCapture = function (selector) {
var method, name,iterator;
if(selector.substr(0,1) === "#") {
method = "getElementById";
name = selector.substr(1);
iterator = function(fn) { fn(document[method](name)); };
} else {
method = "getElementsByTagName";
name = selector;
iterator = function(fn) {
var i,c = document[method](name);
for(i=0;i<c.length;i++){
fn(c[i]);
};
};
myClickCapture.click = function (fn){
iterator(function(e){
e.onclick=fn;
})
}
return myClickCapture;
}
I haven't tested the code, but in theory, it gets you something like this:
myClickCapture("x").click(function(e){ alert("element clicked") });
Hopefully this gives you a sense of the sorts of things jquery is doing under the covers.
document.getElementsByTagName("x")
returns an array of elements having the tagname 'x'.
You have to right event for each element in the returned array.

Categories

Resources