How to use variables in imagesLoaded callback function? - javascript

I'm trying to do something very simple, but I'm obviously doing something wrong. Here is the code:
var targets = Array(
$('.someDiv'),
$('.myDiv')
);
// This "for" loop check if the Class exists and if so it runs a code
for (var i = targets.length - 1; i >= 0; i--) {
// make the target a jQuery object
var $target = targets[i];
// if the target exists
if ( $target.length > 0 )
{
console.log($target);
// run some code after every image is loaded
$target.imagesLoaded( function()
{
console.log($target);
$target.addClass('blue');
});
}
}
which somehow doesn't work.
The JsFiddle example is here
Is it just impossible to pass a variable without hacking imagesLoaded plugin? Or am I missing something?

Good news: Your code works!
var myVar;
$('.myDiv').imagesLoaded( function() {
console.log(myVar);
});
myVar is undefined as you did not define it.
... try var myVar = "Hello World";

If you want the variable to retain the value it had when calling imagesLoaded(), you can use an IIFE:
var myVar = 'someContent';
(function(myVar) {
$('.myDiv').imagesLoaded( function() {
console.log(myVar);
});
})(myVar);
Take this example:
var myVar = 'some content';
(function(myVar) {
setTimeout(function() {
console.log(myVar);
}, 1000);
})(myVar);
myVar = 'some other content';
console.log(myVar);
It will log some other content and then, a bit later some content, as was the initial value.

The problem is that the imagesLoaded callback is fired after you have already iterated over the elements. Which means that by the time the first callback is fired, $target is a reference to the last element in the array.
Fortunately, since you are just trying to get a reference to the element that imagesLoaded was called on, you can get a reference to it with this.
Updated Example
var targets = [$('.someDiv'), $('.myDiv')];
for (var i = 0; i < targets.length; i++) {
var $target = targets[i];
if ($target.length) {
$target.imagesLoaded(function () {
$(this).addClass('blue');
});
}
}
Alternatively, you could also use the $.each() function in order to store the context on each iteration:
Updated Example
var targets = [$('.someDiv'), $('.myDiv')];
$.each(targets, function (i) {
var $target = targets[i];
if ($target.length > 0) {
$target.imagesLoaded(function () {
$target.addClass('blue');
});
}
});

Related

Why can't I remove my event listener?

I have an issue with removeEventListener, it doesn't seem to work at all, I've seen some other questions on this site but I don't get it, can you help me?
displayImg() {
console.log('img')
for (var i = 1; i <= 4; i++) {
var line = "l"+i;
var position = 0;
var addDivLine = document.createElement('div');
addDivLine.className = 'line';
addDivLine.id = line;
document.getElementById('container').appendChild(addDivLine);
for (var j = 1; j <= 7; j++) {
var block = "b"+j;
var element = line+"-"+block;
var addDivBlock = document.createElement('div');
addDivBlock.className = 'block';
addDivBlock.id = element;
document.getElementById(line).appendChild(addDivBlock);
memory.addEvent(element);
};
};
showImage(event) {
event.preventDefault();
memory.clickedBlock++;
var block = event.target.id;
memory.removeEvent(block);
}
addEvent(id){
document.getElementById(id).addEventListener('click', function(){memory.showImage(event)});
},
removeEvent(id){
console.log("remove");
document.getElementById(id).removeEventListener('click', function(){memory.showImage(event)});
},
I am creating div elements then put an eventListener on them, I call the same function to remove the event, I use the same id, is there something that I forgot? I probably don't fully understand how it really works.
Thanks a lot!
In this two lines:
.addEventListener('click', function() { memory.showImage(event) });
and
.removeEventListener('click', function() { memory.showImage(event) });
function() { memory.showImage(event) } are two different functions. You need to provide reference to the same function in both cases in order to bind/unbind listener. Save it so some variable and use in both places:
.addEventListener('click', memory.showImage);
.removeEventListener('click', memory.showImage);
For example using directly memory.showImage will work properly as it's the same function in both cases.
The function looks like the same but its reference would be different. So, define the function in a scope where it's available for both function and use the reference in both case.
var callback = function(){memory.showImage(event)};
addEvent(id){
document.getElementById(id).addEventListener('click', callback);
}
removeEvent(id){
console.log("remove");
document.getElementById(id).removeEventListener('click', callback);
}

Check whether function has run fully before, based on variable

I have a function which "types" out a header title as though it is being typed on the screen.
The typer only starts typing once a particular section of my site is "active" or is seen on the screen.
At present, it takes the outputID aka the area where this text will be typed into. There are two instances of this function being run, each with different outputIDs - I only want the function to run once per outputID.
This is how the function is initially called.
<h2 id="typer-get-in-touch" class="typer" data-text="Get in Toche^^^^^ Touch"></h2>
if(anchorLink == 'contact'){
var outputID = $("#typer-get-in-touch");
textTyping(outputID);
}else if(anchorLink == 'expertise'){
var outputID = $("#typer-expertise");
textTyping(outputID);
}
This is the textTyping function
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
var textArray = textString.split("");
var texttypeing = setInterval(
function() {
typeOutText(outputID,textArray);
}, 170);
function typeOutText(outputID,textArray) {
if (textArray[0] == "^"){
outputID.text(function(index, text){
return text.replace(/(\s+)?.$/, '');
});
textArray.shift();
}else {
if (textArray.length > 0) {
outputID.append(textArray.shift());
} else {
clearTimeout(texttypeing);
}
}
}
}
My issue at present is that the function runs multiple types, and continues to type each time the original anchorLink trigger is achieved. The result is that is writes the title many times e.g:
Get In TouchGet In TouchGet In Touch
Each time the section is navigated to, the typing starts again.
How can I run this function only ONCE per outputID? So once the outputID has been used, the function can no longer run for that data?
JSFiddle of non-working example: https://jsfiddle.net/qLez8zeq/
JSFiddle of mplungjan's solution: https://jsfiddle.net/qLez8zeq/1/
Change
function textTyping(outputID){
$(outputID).show();
var textString = $(outputID).data("text");
to
function textTyping(outputID){
var textString = $(outputID).data("text");
if (textString=="") return;
$(outputID).data("text","");
$(outputID).show();
FIDDLE
What you need to do is to bind the event handler for each ID and then unbind it after it's been triggered the first time. Since you're already using jQuery, you can use the "one" method to do exactly this for each outputID:
$( "#typer-get-in-touch" ).one( "click", function() {
textTyping(outputID);
});
I suppose you could store your processed outputIds into an array and then check if the given outputId is present in the array before starting?
Define your array, check for the existence, if not found, do code example:
var processedIds = [];
function textTyping(outputID) {
var foundItem = false;
for (var i = 0; i < processedIds.length; i++)
{
if (processedIds[i] == outputID) {
foundItem = true;
break;
}
}
if (!foundItem) {
//the rest of your code goes here
}
}
You can add some check at the beginning of your function:
var called = {};
function textTyping(outputID) {
if (called[outputID]) {
return;
}
called[outputID] = true;
// your code
}

Pass HTML element to JavaScript function

I am passed 3 html elements as parameters to JS function. JS function is in separate file. I have problem to bind 'click' event with _confBtn object (which is parameter). My complete JS file:
window.HAS = window.HAS || {};
HAS.MyApp = HAS.MyApp || {};
(function (_this, $, undefined) {
var _sessionTimeOut = false;
var _startCountDown = false;
var _counterTime;
var _countDownTime;
var _dialogWrap;
var _confBtn;
var _counter;
_this.init = function (showDialogTime, logofCountDownTime, dialogWrap, counter, confirmationButton) {
_counterTime = 5;
_countDownTime = 0;
_dialogWrap = $('#' + dialogWrap);
_confBtn = $('#' + confirmationButton);
_counter = $('#' + counter);
alert(_confBtn.text());
createSessionTimeOut();
$(document).bind("mousemove keypress mousedown mouseup", resetTimeOut);
}
_confBtn.on('click', function () {
window.clearInterval(_startCountDown);
_dialogWrap.css('visibility', 'hidden');
createSessionTimeOut();
$(document).bind("mousemove keypress mousedown mouseup", resetTimeOut);
});
function createSessionTimeOut() {
_sessionTimeOut = window.setTimeout(function () {
_dialogWrap.removeAttr("style");
_counter.text(_counterTime);
$(document).unbind("mousemove keypress mousedown mouseup");
startCountDown();
}, 2000);
}
function startCountDown() {
_startCountDown = window.setInterval(function () {
if (_counterTime >= 0) {
_counter.text(_counterTime--);
}
_countDownTime++;
if (_countDownTime >= 4) {
logOutUser();
return;
}
}, 1000);
}
function resetTimeOut() {
window.clearTimeout(_sessionTimeOut);
_sessionTimeOut = false;
createSessionTimeOut();
}
function logOutUser() {
$.ajax({
url: '/MyApp/Account/LogOut',
type: 'GET',
success: function () {
document.location.href = '/MyApp/Account/Login';
}
})
}
}(window.HAS.MyApp.SessionTimeOut = window.HAS.MyApp.SessionTimeOut || {}, jQuery));
I call in separate page like in following:
SessionTimeOut.init('5', '5', 'dialog-wrap', 'confirm-button', 'counter');
I have issue with _confBtn when I try to call click event. Browser show that is undefined.
Please help.
It would probably better to do something more dynamic like this:
function SomeFunction (element1,element2) {
var e1 = $("#"+element1),
e2 = $("#"+element2);
// Do something with variables e1 and e2
}
and you would call like this:
//html:
<div id="one"><div>
<div id="two"><div>
//javasctript:
SomeFunction('one','two');
No, you are mixing a function declaration with a function call somehow. You can't provide function arguments when defining a function. This however will work fine:
function someFunction($element1, $element2) {
//Do something with the elements
}
someFunction($("#element1"), $("#element2"));
Note that $element1 and $element2 are just variable names, and the leading $ doesn't have anything to do with jQuery. It is just a common convention to identify variables referencing jQuery selections.
You can of course do it, just by using the normal jQuery way of including multiple selectors. Your code is slightly incorrect because you are actually only defining the function without calling it, and you are not supposed to pass arguments/variables into the function when defining it.
Unless you have the intention to distinguish between two groups of elements, I would refrain from declaring elements individually as you have used in your question, because sometimes you will never know the length of the selected items.
function someFunction($ele) {
// jQuery objects will be accessible as $ele
}
// Call the function
someFunction($('#selector1, #selector2'));
However, if the former is the case, you can always do so:
function someFunction($ele1, $ele2) {
// jQuery objects will be accessible as $ele1 and $ele2 respectively
// Example: $ele1.hide();
// $ele2.show();
}
// Call the function
someFunction($('#selector1'), $('#selector2'));
For example, you can refer to this proof-of-concept JSfiddle: http://jsfiddle.net/teddyrised/ozrfLwwt/
function someFunction($ele) {
// jQuery objects will be accessible as $ele
$ele.css({
'background-color': '#c8d9ff'
});
}
// Call the function
someFunction($('#selector1, #selector2'));
If you want to pass some elements to function you can use jQuery constructor to standardize arguments
function SomeFunction (element1,element2) {
element1 = $(element1);
element2 = $(element2);
// and you have 2 jQuery objects...
}
// and now you can pass selector as well as jQuery object.
SomeFunction($('div.a'),'#b');
You can pass paramters as much as you want this way. I use jQuery in this code and created a simple function.
var item=$("#item-id");
var item1=$("#item1-id");
makeReadOnly(item,item1);
function makeReadOnly(){
for(var i=0;i<arguments.length;i++){
$(arguments[i]).attr("readonly", true);
}
}

How to call function outside of jQuery(document).ready with setTimeout()?

My code looks something like:
$(document).ready(function(){
var cont = 0;
function func1(cont)
{
//Some code here
search.setSearchCompleteCallback(this, searchComplete, null);
//Some other code
}
func1(cont);
function searchComplete()
{
//Some code
cont += 1;
if (cont < length ) {
func1(cont);
} else {
// Other code
}
}
});
So what I want to do is delay the execution of func1(cont); inside of the searchComplete() function. The reason for this is that all the code does is to work with the Google search API and PageRank checks and I need to slow down the script so that I won't get banned. (Especially for the requests it makes regarding the PR check).
If I simply use setTimeout() on func1(cont); it says there is no func1() defined, if I try to get the function outside $(document).ready() it sees the function but the Google code won't for for it needs the page completely loaded.
How can I fix setTimeout or how can I pause the script for a number of seconds ?
Thanks!
Write
func1(cont);
as
window.setTimeout(function() {
func1(cont);
}, 1000);
Instead of declaring the function like this:
function func1(cont) {}
declare it like this:
var func1 = function(cont) {}
You'll need to rearrange your code a little:
$(document).ready(function(){
var cont = 0;
var func1;
var searchComplete = function()
{
//Some code
cont += 1;
if (cont < length ) {
func1(cont);
} else {
// Other code
}
}
func1 = function(cont)
{
//Some code here
search.setSearchCompleteCallback(this, searchComplete, null);
//Some other code
}
func1(cont);
});
I'd try something like this. I prefer to declare the vars and functions inside the jquery namespace, but you could equally move the cont variable and the functions outside of the document ready function and have them available globally.
$(document).ready(function(){
$.cont = 0;
$.func1 = function() {
//Some code here
search.setSearchCompleteCallback(this, $.searchComplete, null);
//Some other code
}
$.searchComplete = function() {
//Some code
$.cont += 1;
if (cont < length ) {
setTimeout($.func1,1000);
} else {
// Other code
}
}
setTimeout($.func1,1000); // delay the initial start by 1 second
});
Hopefully I've got your description correct:
document.ready() event fires
Inside document.ready() you want a function to be called after X milliseconds
This function wires up the Google object search.setSearchCompleteCallback() to another function (which it looks like it needs a parent object from the this)
If this is the case, why do you need any of the functions declared inside the document.ready() scope? Can you't simply make all 3 global? e.g.
var search = null; // initialise the google object
var cont = 0;
function timedSearch()
{
search.setSearchCompleteCallback(this, searchComplete, null);
}
function searchComplete()
{
if (++cont < length) // postfix it below if this is wrong
setTimeout(timedSearch,1000);
}
$(document).ready(function()
{
setTimeout(timedSearch,1000);
}
Hit me with the downvotes if I've misunderstood.

onchange javascript variable

Is there a way to call a JavaScript function if a javascript variable changes values using jQuery?
Something to the extend of -
var test = 1;
test = 2; // calls a javascript function
test = 3; // calls a javascript function
This way I wouldn't have to add an onchange event to so many different functions.
(Something seems a bit carelessly planned in your code if you need functionality like that)
The easiest way to add that feature is to create a function for updating your variable, that also calls whatever other function you want to.
Instead of:
var test = 1;
test = 2; // calls a javascript function
test = 3; // calls a javascript function
You do:
var test = 1;
function set_test(newval) {
test = newval;
my_callback(); // this is whatever you wanted to call onChange
}
set_test(2);
set_test(3);
try this, it's real variable change event:
var book = {
_year: 2004,
edition: 1
};
Object.defineProperty(book, "year", {
get: function(){
return this._year;
},
set: function(newValue){
this._year=newValue;
this.edition=newValue-2004;
alert(this._year);
alert(this.edition);
}
});
book.year=2017
// will alert 2017 and 13
No, there is not, just polling with setInterval or setTimeout or callbacks. Events only apply to DOM. I'd suggest that you try to go with callbacks and do things like this:
function foo(data, callback)
{
// do things with data
callback(data);
}
function bar(data)
{
console.log('callback can has', data);
}
foo('baz', bar);
It's a rough example, but should give you the idea.
One option is to wrap your data into a heavier object.
var Watching = function(){
var a;
this.getA(){
return a;
};
this.setA(value){
a = value;
this.trigger('watch');
};
his.watchA(callback){
this.bind('watch', callback);
};
};
var obj = new Watching();
obj.watchA(function(){ alert('changed'); });
obj.setA(2);
This doesn't answer your question exactly, but it may solve your problem:
make your variable as html content of an element, then use jQuery change() event
<script>
document.write("<div id='test'>"+test+"</div>";
$("#test").change(function(){//your script here});
</script>
You can create a class to be notified when your variable changed.
this is the class:
class ListeningVariable {
constructor(val, changeHandler) {
this.val = val;
this.changeHandler = changeHandler
}
set value(val) {
if (this.val !== val) {
this.changeHandler(val);
}
this.val = val;
}
changeHandler(val) {}
}
Then you can create an instance of this class instead of your variable:
let myVar = new ListeningVariable(25/*initialize*/, function(val) {
console.log("variable Changed to:", val);
}/*handler function*/);
And when you want to change your variable, just use this code:
myVar.value = 20; // calls the changeHandler function
myVar.value = 20; // does't call the changeHandler function
myVar.value = 40; // calls the changeHandler function
You can do something like this with setting intervals to keep track of change:
var dataToChange = 1;
var key = dataToChange;
var int = setInterval(() => {
if (dataToChange != key) {
console.log('changed'); /// if data changes
clearInterval(int);
} else {
console.log('nothing changed'); /// while nothing changes
}
}, 3000);
setTimeout(() => {
///// supposedly this is when the variable changes
dataToChange = 2;
}, 9000);
The below function will poll for changes in the test variable every 5 seconds:
// initialize test variable globally
var test = 1;
// global variable to store the previous value of test
// which is updated every 5 seconds
var tmp = test;
setInterval("pollForVariableChange()", 5000);
function pollForVariableChange() {
if (tmp != test) {
alert('Value of test has changed to ' + test);
}
tmp = test;
}

Categories

Resources