Accessing js variable inside a separate callback (function) - javascript

I'm using a jquery script called magnific popup and I'm trying to access a variable I created in a callback function, inside another callback function but I can't work out how to do it. My code for the magnific init looks like this:
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
var communityClass = item.community;
console.log(item.community);
// this ^ does actually print the data-community
console.log('Parsing content. Item object that is being parsed:', item);
},
resize: function() {
console.log('Popup resized');
// resize event triggers only when height is changed or layout forced
$('.mfp-bg').addClass(communityClass);
}
}
});
If I try and set $('.mfp-bg').addClass(communityClass); or $('.mfp-bg').addClass(item.community); I get a Uncaught ReferenceError: communityClass is not defined.
I can't apply a class to mfp-bg inside elementParse as that element hasn't been created yet.
I know that I can't use variables from different functions in javascript, but I'm a bit stuck at this point on how I can actually use the data-community attribute inside the resize callback, because it seems like I can only create the variable inside the elementParse callback?
Any help would be much appreciated, cheers.

You could create a global variable, outside the function and assign item.community to it. That way you will be able to access it in the other callback aswell
For example:
var communityClass;
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
communityClass = item.community;
console.log(item.community);
// this ^ does actually print the data-community
console.log('Parsing content. Item object that is being parsed:', item);
},
resize: function() {
console.log('Popup resized');
// resize event triggers only when height is changed or layout forced
$('.mfp-bg').addClass(communityClass);
}
}
});

I realised after console.logging this there was already a currItem bit I could access, so I changed the code to this and it works fine now.
$('.packery').magnificPopup({
delegate: '.js-modal',
type: 'ajax',
mainClass: 'mfp-zoom-in',
removalDelay: 500, //delay removal by X to allow out-animation
callbacks: {
elementParse: function(item) {
item.community = item.el.attr('data-community');
},
resize: function() {
$('.mfp-bg').addClass(this.currItem.community);
}
}
});

Related

CropperJS getCroppedCanvas() returns null on second initialization

The method is called in the ready event. On the first call it works. When the modal is closed, I am destroying the cropper - cropper.destroy() . After opening the modal second time, the cropper is initialized again but this time cropper.getCroppedCanvas() returns null
let cropper = new Cropper(image, {
dragMode: 'move',
aspectRatio: ratio,
restore: false,
guides: false,
center: false,
highlight: false,
cropBoxMovable: false,
cropBoxResizable: false,
toggleDragModeOnDblclick: false,
ready: function () {
modal.find(".save").on("click", function () {
console.log(cropper.getCroppedCanvas())
cropper.getCroppedCanvas().toBlob(function (blob) {
let formData = new FormData()
formData.append("croppedImage", blob)
jQuery.ajax({
method: "post",
url: "index.php?option=com_image_slideshows&task=slideshow.saveCroppedImage",
data: formData,
processData: false,
contentType: false
})
.done(function (response) {
modal.modal("hide")
})
})
})
cropper.crop()
}
})
On modal closing this happens:
modal.on("hidden.bs.modal", function () {
cropper.destroy()
jQuery("#cropper-modal .modal-body").html(
jQuery("<img>", {
id: "image",
"class": "cropper-hidden"
})
)
})
My guess would be that the cropper variable you set initially:
let cropper = new Cropper(...)
is still being referenced in your ready function the second time around. I would first try ensuring that the cropper variable is set to null after cropper.destroy() is called.
You could also try accessing the correct Cropper instance in your ready function by accessing this.cropper, for example:
ready: function () {
modal.find(".save").on("click", function () {
console.log(this.cropper.getCroppedCanvas());
}
}
Besides calling destroy() method you also need to reinitialize the event listeners.
I think that event listeners still hold a reference to the old cropper so you need to unbind() them first and create them again on every ready() function call. I also suggest using this pointer instead of variable cropper to ensure you access the current instance.
ready: function () {
var that = this;
modal.find(".save").unbind('click');
modal.find(".save").on("click", function () {
console.log(that.cropper.getCroppedCanvas());
}
}
#jmfolds is correct, it is important that you reference this.cropper.
However, that is not enough. You have to verify that the cropper is ready for the second cropping instance even if the function is defined in the "ready" event of the cropper and assuming that the first cropper is destroyed.
You can do that easily using an if statement:
if (this.cropper.ready === true) {
// Your code as is
} else {
return
}
This solved my problem when I was implementing it.
In your destroy function do you remove the .on event too?
modal.find(".save").off();
After the cropper.destroy(); did you validate that it is really destroyed?
maybe set in the variable cropper too null or undefined too after destroy.
So my 2 cents, It looks all correct so far I see.

iScroll page currently scrolling to detection

I use the following iScroll 5 code (generally, not so important: just a common scrolling page-by-page):
var myScroll = new IScroll('.scroller', {
mouseWheel: true,
scrollbars: true,
keyBindings: {
// doesn't matter
},
preventDefault: false,
fadeScrollbars: true,
snap: 'section', // <-- that's the key
wheelAction: 'scroll',
});
myScroll.on('beforeScrollStart', function (e) {
myScroll.preventDisabling = true;
});
myScroll.on('scrollMove', function (e) {
});
myScroll.on('scrollStart', function (e) {
// !!! I need the detection somewhere here !!!
if (!myScroll.preventDisabling) {
myScroll.disable();
disabledWasCalledInMeanwhile = true;
}
myScroll.preventDisabling = false;
});
var disabledWasCalledInMeanwhile = false;
// that's just to prevent jumping to another page before scrolling is finished
myScroll.on('scrollEnd', function (e) {
disabledWasCalledInMeanwhile = false;
window.setTimeout(function () {
if (!disabledWasCalledInMeanwhile)
myScroll.enable();
}, 250);
$('.labels>*').toggleClass('active', false)
.eq(this.currentPage.pageY).toggleClass('active', true);
});
myScroll.on('scrollCancel', function (e) {
myScroll.enable();
});
So, is there any chance to detect in beforeScrollStart or scrollStart the page I am going to scroll to? That's important to know for triggering that page items animation. Thanks!
I've used iScroll for a number of years (it is a excellent library), and I don't know of a built-in method of doing it. All the scroll events (except scrollEnd) before the iScroll snap is determined. However, with a slight modification of the library, I believe it is possible.
First, go into iScroll.js source and find the _nearestSnap method. At the bottom of the method, you will find the object you seek returned. Before the return, grab that data and pass it to a custom event. Unfortunately, iScroll's event system doesn't permit you to pass custom variables to events, so you'll have to do a work-around. In addition, you'll need to track the "flick" event because it won't trigger the _nearestSnap method.
iScroll modification in _nearestSnap method
this.customSnap({
x: x,
y: y,
pageX: i,
pageY: m
});
Update to class instance. Note the addition of "customSnap" method and the flick event.
myScroll = new IScroll('#wrapper', {snap: "p"});
myScroll.customSnap = function(data) {
console.log(data);
};
myScroll.on('flick', function() {
console.log(data.currentPage);
});
That should do it. Not necessarily the cleanest update, but in my testing, it does work.
http://jsfiddle.net/9pa4th4y/

Modify text with jQuery for dynamically added elements in DOM

I have a page http://www.projectdemocracy.in/ where some elements are dynamically added (example - CSS class ".fyre-comment-count" showing "X Comment").
I wanted to modify the text once page is loaded.
I was able to change the text using console of jQuery
$(".fyre-comment-count").html($(".fyre-comment-count").text().split(" ")[0]+" Conversations");
When I type the same in my page, it doesn't work.
I also tried $(document).ready(function(){}); but no luck (same with document.ajaxSuccess).
Similarly, document.on would be working only with 'click' events but I want it to be done by default.
$(document).on('click', '.fyre-comment-article', function() {
//Code
});
Does it have any 'load' type event in document.on?
What should I do to accomplish this?
Thanks,
I analyzed your web page and saw the following code:
var articleId = fyre.conv.load.makeArticleId(null);
fyre.conv.load({}, [{
el: 'livefyre-comments',
network: "livefyre.com",
siteId: "351251",
articleId: articleId,
signed: false,
collectionMeta: {
articleId: articleId,
url: "http://projectdemocracy.in",//fyre.conv.load.makeCollectionUrl(),
}
}], function() {});
It seems like the 3rd argument of fyre.conv.load method is a callback function that will be executed when all elements are generated.
So put your code inside this function. It will be like this:
...
}], function() {
console.log('callback');
$(".fyre-comment-count").html($(".fyre-comment-count").text().split(" ")[0]+" Conversations");
});
Hope this helps!
EDIT
If it still doesn't work it may be because livefyre runs this callback before DOM elements are being created. The workaround is to put $(".fyre-comment-count").html(...); inside setTimeout with minimal delay:
...
}], function() {
setTimeout(function() {
$(".fyre-comment-count").html($(".fyre-comment-count").text().split(" ")[0]+" Conversations");
}, 10); //minimal delay. wait till livefyre generates all needed elements
});
EDIT 2
This is another variant how to make it work:
...
}], function(widget) {
widget.on('commentCountUpdated', function() {
setTimeout(function() {
$(".fyre-comment-count").html($(".fyre-comment-count").text().split(" ")[0]+" Conversations");
}, 10);
});
});
This widget has a commentCountUpdated event, so you have to subscribe on it, and every time the comments number is changing, your callback will be executed
By the way, commentCountUpdated callback recieves a single argument - the number of comments, so you can rewrite your code as follows:
}], function(widget) {
widget.on('commentCountUpdated', function(commentsNum) {
setTimeout(function() {
$(".fyre-comment-count").html(commentsNum + " Conversations");
}, 10);
});
});
jQuery has a on load event, if this is what you are looking for:
$(window).load(function(){ ... });

Inheritance causes function to be executed so many times Javascript

I am learning JavaScript and I got stuck creating a function to minimize a window. The problem is that this functions seems to stack in itself so many times.
Gere is my principal function :
function displayChatWindow(user, status, avatar, id){
var template = _.template($("#windowTemplate").html(), {userName: user, userStatus: status, userAvatar: avatar, userId: id});
stackingWidth = stackingWidth - boxWidth;
console.log(stackingWidth);
$("body").prepend(template);
$(".messages-container").slimScroll({
height: '200',
size: '10px',
position: 'right',
color: '#535a61',
alwaysVisible: false,
distance: '0',
railVisible: true,
railColor: '#222',
railOpacity: 0.3,
wheelStep: 10,
disableFadeOut: false,
start: "bottom"
});
$("#" + id).css({
top: absoluteY,
left: stackingWidth
});
$(".minimize-others").on("click", displayOthersChat);
$(".chat input, .chat textarea").on("focus", cleanInputs);
$(".chat input, .chat textarea").on("blur", setInputs);
}
This function receives some parameters and with a template creates the chat window. At the end it applies the function to minimize the window (displayOthersChat) and load plugins and stuff for each window.
My displayOtherChats function:
function displayOthersChat(e){
/*e.preventDefault();*/
var This = $(this).parent().parent();
var minimize = This;
if(!This.hasClass("draggable")){
This.animate({
top: windowHeight - boxHeight - 20
});
This.addClass("draggable");
This.draggable({handle: ".header"});
var timeOut = setTimeout(function() {
This.find(".minimize").toggleClass("rotate");
}, 500);
}else{
This.draggable("destroy");
This.removeClass("draggable");
var timeOut = setTimeout(function() {
This.find(".minimize").toggleClass("rotate");
}, 500);
This.animate({
top: absoluteY
});
}
/*return false;*/
}
This seems to work really well. If I open my first window it displays and also minimizing the window works. When I open another window, the last window works correctly but the first window opens when I try to minimize it.
It seems that it calls the function twice, and if I open a third window, the first window calls the function three times.
I actually don't know whats going on, I will appreciate if you guys could help me. I also leave a link so you guys can see whats going on: http://s3.enigmind.com/jgonzalez/nodeChat.
The problem seems to be that you are binding the same event handler to the same elements over and over again.
$(".minimize-others").on("click", displayOthersChat); will bind displayOthersChat to all existing elements with class minimize-others. .on always adds event handlers, it does not replace them. So if you call displayChatWindow multiple times, you are binding the event handler to the .minimize-others elements multiple times.
You only want to bind the handler to the window that was just created, for example:
// create reusable jQuery object from HTML string.
var $template = $(template).prependTo('body');
// instead of $("body").prepend(template);
// ...
$template.find('.minimize-others').on('click', displayOthersChat);
Same goes for the other event handlers.
Alternatively, you could bind the event handler once, outside of the function and use event delegation to capture the event:
$(document.body).on('click', '.minimize-others', displayOthersChat);

use custom function configuration option of a jquery script

I am using this jQuery plugin: http://demo.awkwardgroup.com/showcase/
I am trying to show a counter on the first slide of Awkward Showcase and have been unable to nest the call into the script without breaking it to some degree or other. I tried using current_id, but realized that the value of current_id was inconsistent, at least, when I was calling for it. I am now trying to rely on the "active" state of the number 1, which corresponds to the first slide and is consistent.
I realize now that it has a custom_function feature, but don't know how to plug this in there
var awNavButtonID = document.getElementById('showcase-navigation-button-1');
function myHack() {
var awNavButtonClass = awNavButtonID.className;
if(awNavButton == 'active'){
document.getElementById('defaultCountdown').style.left = "250px";
} else {
document.getElementById('defaultCountdown').style.left = "-9999px";
}
}
$(document).ready(function(){
$("#showcase").awShowcase(
{
content_width: 700,
content_height: 470,
custom_function: null //how do i plug it in here?
});
});
If you want to bind myHack function to the custom_function callback, try this :
$("#showcase").awShowcase(
{
content_width: 700,
content_height: 470,
custom_function: myhack
});
You can also attach your function to a local variable.
var myHackFunction = function myHack() { ... }
...
custom_function: myHackFunction
Finally, you can implement an anonymous function :
custom_function: function() { /* do stuff here */ }

Categories

Resources