Probably misunderstanding something simple, but i can't seem to get this to work.
I want to: go trough each img element in "wrapper", and strip all html from the title attribute (with stripTags() from mootools more). I get the error:
"this.get is not a function"
here is the code:
$('wrapper').getElements('img').each(function() {
var oldAlt = this.get('title').stripTags();
this.setProperty('alt', oldAlt);
});
Thanks in advance
$('wrapper').getElements('img').each(function(el) {
var oldAlt = el.get('title').stripTags();
el.setProperty('alt', oldAlt);
});
this does not refer to the looping element -- the first argument to the .each callback function is element passed, the second is index (opposite to jquery where index is first).
The other option is to bind the this variable
$('wrapper').getElements('img').each(function() {
var oldAlt = this.get('title').stripTags();
this.setProperty('alt', oldAlt);
}).bind(this);
The extra .bind(this) basically means, in the scope inside the each function, the variable this is bound to whatever value is refers to outside. (like passing the variable into the scope). If you have need to access the normal this pointer as well, as the outside reference, you should go with an option such as #Chetan's answer
Related
When dynamically creating an element of type select, there are two problems when setting the onclick method:
It is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";
This results in a final HTML tag where no onclick is shown at all.
When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.
However the functionality seems to be broken, as the argument always evaluates to undefined
Here a simple example of my problems:
var selectElem = document.createElement("select");
selElem.id="articleSelector_"+this.articleIndex;
console.log("the index of the article is " + this.articleIndex);
selElem.setAttribute("onclick","updateInput(this.articleIndex);");
//selElem.onclick="updateInput(this.articleIndex)"; //this does not work
The log shows the correct number. Inside the updateInput method, the argument is of value undefined instead of the number previously shown in the log.
Try attaching handlers with pure Javascript, and not with HTML, without onclick = "... (which is as bad as eval).
The this in your script refers to the calling context of the function - what is it?
You might want:
element.addEventListener('click', () => {
updateInput(this.articleIndex);
});
(arrow functions retain the this of their surrounding scope)
it is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";
What that code does is it assigns the string "updateInput(this.articleIndex)" to the onclick which makes no sense and certainly not what you want.
Even if you remove the quotes:
element.onclick = updateInput(this.articleIndex);
It is still incorrect because it assigns the result of the updateInput() function to the onclick which is again not what you want.
You need to assign a function name to the onclick like this:
element.onclick = updateInput;
However, this doesn't allow you to pass a parameter as you wish. To do so, you need to use an anonymous function:
element.onclick = function() {
updateInput(this.articleIndex)
};
When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.
This works because it sets the attribute onclick and it is a string type, so everything is correct. It is equivalent to using the anonymous function above. The only difference is this, which in this case refers to the element itself, while in the above code it depends on the context that the code appears in. That's why in this case the argument always evaluates to undefined because the select element doesn't have an articleIndex property.
The problem is the value of the context this when that element is clicked, the context this is not available anymore at that moment.
You have two ways to solve this problem:
You can use the function addEventListener to bind the event click, and bind the function/handler with the desired context this:
The function bind binds a specific context to a function.
selElem.addEventListener('click', updateInput.bind(this));
function updateInput() {
console.log(this.articleIndex);
}
As you need a specific value, you can use data attributes. That way, you don't need to worry about the context this.
selElem.dataset.articleIndex = this.articleIndex;
selElem.addEventListener('click', function() {
updateInput(this.dataset.articleIndex); // Here you can get that value.
});
Amongst other things, I have read:
what-does-this-mean
you-must-remember-this
mythical-methods
but they haven't solved 'this' problem I'm having with a piece of JavaScript.
I have a Section object that gets passed some XML which it uses to populate the section. In the Section object I append a div which has a specified index. The resulting jQuery object is pushed into a sections Array. The following code is from the Section object code:
sections.push($('#section' + p_sectionIndex));
this.showSection = function() {
this.show();
}
this.hideSection = function() {
this.hide();
}
sections[sections.length-1].on('show', this.showSection.call(sections[sections.length-1]));
sections[sections.length-1].on('hide', this.hideSection.call(sections[sections.length-1]));
Elsewhere I call sections[index].trigger('hide'); and sections[index].trigger('show');
The first of the links I mentioned above seemed to suggest this in a function depends on HOW it's called and that you could pass a reference to this into the function by using call. I know the showSection and hideSection function ARE being triggered - I just can't get the this in those functions to refer to the jQuery objects in the sections Array.
I have tried multiple variations of the above (excluding the call, using $(this) in the functions, adding the showSection and hideSection functions to the jQuery object - amongst others) but I'm kind of out of ideas.
Any help much appreciated!
this in an event handler is the element node that the event was bound to. If you want a jQuery object wrapping that node, use $(this)
Demo: http://jsfiddle.net/b36M6/
This of course assumes you revert back to the correct way of passing a function to the event binding.
When you use .call(), you're invoking the function immediately.
Since you want this to refer to the element, bound, just pas the function itself.
sections[sections.length-1].on('show', this.showSection);
sections[sections.length-1].on('hide', this.hideSection);
Now this in the showSection and hideSection methods will refer to the sections[] member to which it was bound.
I assume "show" and "hide" are some sort of custom events.
I would like to replace some "this" in a script.
$(this).find('option').each(function() {
$(this).hide();
})
Is there a possibility to replace only the outer this, or only the "this" that are not inside a function block? My idea doesn't work ...
.replace(/([^{])\bthis\b([^}])/gm, $1replacement$2)
addendum: The first code is handled as a string, not as javascript!
I am search for a regexp to replace only the outer "this".
If I understand your issue correctly, you are wanting to refer to the $(this) variable from within your function, however that variable gets changed to the local scope of the function. So within the function, $(this) will refers to the item you are currently iterating over.
You'll want to cache the scope of the $(this) object before you enter your function.
var cached_this = $(this);
$(this).find('option').each(function() {
cached_this.hide();
})
Now you have access the value of the outer $(this) from within a different scope using the cached_this variable.
The way to distinguish "outer" from "inner" text is that for inner, the next curly bracket is a right one, which closes the block its in.
So, using a negative look ahead to exclude this within a block:
str = str.replace(/\bthis\b(?![^{]*\})/gs, replacement);
Also note the simplification of the replacement term, since the match is just the target text "this".
See a live demo of this regex working.
In that case this is an object. It's not a string that you can replace. You may assign another value, but such a replacement will not work.
If you want to replace only certain divs, simply change your jQuery selector to select those with a certain class, or child elements of a parent div, for example.
You can try to use call. Due to this function you can replace this object:
$(this).find('option').each(function() {
// here this === replacement
$(this).hide();
}.call(replacement))
If it's in a function, you can "replace" this by assigning a different variable to it when you call it.
var func = function() {
$(this).find('option').each(function() {
$(this).hide();
});
};
func.apply(annotherThis);
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
}
The Objective
I want to dynamically assign event handlers to some divs on pages throughout a site.
My Method
Im using jQuery to bind anonymous functions as handlers for selected div events.
The Problem
The code iterates an array of div names and associated urls. The div name is used to set the binding target i.e. attach this event handler to this div event.
While the event handlers are successfully bound to each of the div events, the actions triggered by those event handlers only ever target the last item in the array.
So the idea is that if the user mouses over a given div, it should run a slide-out animation for that div. But instead, mousing over div1 (rangeTabAll) triggers a slide-out animation for div4 (rangeTabThm). The same is true for divs 2, 3, etc. The order is unimportant. Change the array elements around and events will always target the last element in the array, div4.
My Code - (Uses jQuery)
var curTab, curDiv;
var inlineRangeNavUrls=[['rangeTabAll','range_all.html'],['rangeTabRem','range_remedial.html'],
['rangeTabGym','range_gym.html'],['rangeTabThm','range_thermal.html']];
for (var i=0;i<inlineRangeNavUrls.length;i++)
{
curTab=(inlineRangeNavUrls[i][0]).toString();
curDiv='#' + curTab;
if ($(curDiv).length)
{
$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
$(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
}
}
My Theory
I'm either not seeing a blindingly obvious syntax error or its a pass by reference problem.
Initially i had the following statement to set the value of curTab:
curTab=inlineRangeNavUrls[i][0];
So when the problem occured i figured that as i changed (via for loop iteration) the reference to curTab, i was in fact changing the reference for all previous anonymous function event handlers to the new curTab value as well.... which is why event handlers always targeted the last div.
So what i really needed to do was pass the curTab value to the anonymous function event handlers not the curTab object reference.
So i thought:
curTab=(inlineRangeNavUrls[i][0]).toString();
would fix the problem, but it doesn't. Same deal. So clearly im missing some key, and probably very basic, knowledge regarding the problem. Thanks.
You need to create a new variable on each pass through the loop, so that it'll get captured in the closures you're creating for the event handlers.
However, merely moving the variable declaration into the loop won't accomplish this, because JavaScript doesn't introduce a new scope for arbitrary blocks.
One easy way to force the introduction of a new scope is to use another anonymous function:
for (var i=0;i<inlineRangeNavUrls.length;i++)
{
curDiv='#' + inlineRangeNavUrls[i][1];
if ($(curDiv).length)
{
(function(curTab)
{
$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
$(curDiv).bind("mouseout", function(){showHideRangeSlidingTabs(curTab, false);} );
})(inlineRangeNavUrls[i][0]); // pass as argument to anonymous function - this will introduce a new scope
}
}
As Jason suggests, you can actually clean this up quite a bit using jQuery's built-in hover() function:
for (var i=0;i<inlineRangeNavUrls.length;i++)
{
(function(curTab) // introduce a new scope
{
$('#' + inlineRangeNavUrls[i][1])
.hover(
function(){showHideRangeSlidingTabs(curTab, true);},
function(){showHideRangeSlidingTabs(curTab, false);}
);
// establish per-loop variable by passsing as argument to anonymous function
})(inlineRangeNavUrls[i][0]);
}
what's going on here is that your anonmymous functions are forming a closure, and taking their outer scope with them. That means that when you reference curTab inside your anomymous function, when the event handler runs that function, it's going to look up the current value of curTab in your outer scope. That will be whatever you last assigned to curTab. (not what was assigned at the time you binded the function)
what you need to do is change this:
$(curDiv).bind("mouseover", function(){showHideRangeSlidingTabs(curTab, true);} );
to this:
$(curDiv).bind("mouseover",
(function (mylocalvariable) {
return function(){
showHideRangeSlidingTabs(mylocalvariable, true);
}
})(curTab)
);
this will copy the value of curTab into the scope of the outer function, which the inner function will take with it. This copying happens at the same time that you're binding the inner function to the event handler, so "mylocalvariable" reflects the value of curTab at that time. Then next time around the loop, a new outer function, with a new scope will be created, and the next value of curTab copied into it.
shog9's answer accomplishes basically the same thing, but his code is a little more austere.
it's kinda complicated, but it makes sense if you think about it. Closures are weird.
edit: oops, forgot to return the inner function. Fixed.
I think you're making this more complicated than it needs to be. If all you're doing is assigning a sliding effect on mouseover/out then try the hover effect with jquery.
$("#mytab").hover(function(){
$(this).next("div").slideDown("fast");},
function(){
$(this).next("div").slideUp("fast");
});
If you posted your full HTML I could tell you exactly how to do it :)
You can put your variable's value into a non existing tag, and later you can read them from there. This snippet is part of a loop body:
s = introduction.introductions[page * 6 + i][0]; //The variables content
$('#intro_img_'+i).attr('tag' , s); //Store them in a tag named tag
$('#intro_img_'+i).click( function() {introduction.selectTemplate(this, $(this).attr('tag'));} ); //retrieve the stored data