I'm totally newbie in jQuery and i wonder if it is possible to combine these two functions.
As you can see, the first function is used to load json data to trigger a click.
The second function is used to toggle view for the list items.
Could you help me, and show me the good way to combine these functions!?
When the json file is loaded, it will be create the list elements (li), and the toggle will be able to toggle these list elements (li).
IMPORTANT: actually, my code don't work (the toggle function not work fine).
Here is the code of 1st functions :
$(document).ready(function() {
// ----------------------
// JSON INFOS
// ----------------------
$(".color-list.one li:first-child").on('click', function() {
$.getJSON("result.json", function(data) {
//Handle my response
$('ul.elements-list').html(
'<li class="elements-item"><span class="tog">' + data.name + '</span><div class="togcont hidden">' + data.info + data.size + '</div></li>');
//alert(data);
});
});
});
The code of 2nd function :
$(document).ready(function() {
// ----------------------
// TOGGLE BULLZ
// ----------------------
$(".tog").click(function(){
var obj = $(this).next();
if($(obj).hasClass("hidden")){
$(obj).removeClass("hidden").slideDown();
$(this).addClass("bounce");
} else {
$(obj).addClass("hidden").slideUp();
$(this).removeClass("bounce");
}
});
});
When you use $(".tog").click() it only binds to whatever elements match the ".tog" selector at that moment so won't work on elements that you add dynamically later. You can instead use the delegated syntax of .on() like this:
$('ul.elements-list').on("click", ".tog", function(){ ...
...which will bind the click handler to your list, but only execute your function if the click occurred on an element in that list that matches the ".tog" selector in the second parameter at the time of the click. And within the handler this will be set to the ".tog" element that was clicked.
Also you can put all your code in a single document ready handler assuming all the code is in the same file.
Also your obj variable is a jQuery object, so you can call jQuery methods on it directly like obj.hasClass() rather than wrapping it in $() again as $(obj).hasClass().
So try this instead:
$(document).ready(function() {
$(".color-list.one li:first-child").on('click', function() {
$.getJSON("result.json", function(data) {
//Handle my response
$('ul.elements-list').html(
'<li class="elements-item"><span class="tog">' + data.name + '</span><div class="togcont hidden">' + data.info + data.size + '</div></li>');
});
});
$('ul.elements-list').on("click", ".tog", function(){
var obj = $(this).next();
if(obj.hasClass("hidden")){
obj.removeClass("hidden").slideDown();
$(this).addClass("bounce");
} else {
obj.addClass("hidden").slideUp();
$(this).removeClass("bounce");
}
});
});
Related
I've created a custom modal dialog that is added and removed from the screen when it is called. However, when I'm trying to remove it, the remove function doesn't seem to be working in certain circumstances.
This is the close function from the modal (triggered by clicking on the close button):
function modal_close() {
$('.custom_block_page').fadeOut().remove();
$(this).parent().fadeOut().remove();
};
This is how I call that function from the button inside the modal dialog:
MatchGame.closeWin = function() {
$('.custom_modal_close').trigger('click');
MatchGame.playGame();
};
If I just click the close button, the dialog is removed and everything works as expected. But when I trigger the close, the dialog fades to nothing, but remains in the body so it displays again the next time it is called.
Checking the console between I get:
$('.custom_block_page').length
1 // displayed the first time
$('.custom_block_page').length
0 // during the 2nd game (expected)
$('.custom_block_page').length
2 // displayed after the 2nd game; I expect this to be 1
I've tried putting a timeout on my playGame, but that didn't seem to help either.
Thanks for the help!
The issue you observe is due to .fadeOut(), which is implemented asynchronously in a whole series of later event threads.
Meanwhile, in the original event thread .remove(), subsequent statements, returning from the function and subsequent statements in the function's caller, ALL execute synchronously - well before .fadeOut() has completed.
The solution is to exploit .promise(), which will return a jQuery promise, from which you can chain .then() :
function modal_close() {
return $('.custom_block_page').add($(this).parent()).fadeOut().promise().then(function() {
$(this).remove();
});
};
In the caller, .trigger() returns jQuery but you now need to work with the returned promise, therefore use .triggerHandler().
MatchGame.closeWin = function() {
$('.custom_modal_close').triggerHandler('click').then(function() {
MatchGame.playGame();
});
};
Edit:
Code from add_block_page() and add_popup_box() can be safely rolled into show_modal_box() to make one larger function.
By doing so, you will benefit from being able to access the variables $block_page, $pop_up, $close, $inner from the close button's click handler.
function show_modal_box() {
var $block_page = $('<div class="custom_block_page"></div>').appendTo('body'); // dark background
var $pop_up = $('<div class="custom_modal_box"></div>').appendTo($block_page);
var $close = $('').appendTo($pop_up);
var $inner = $('<div class="custom_inner_modal_box">loading...</div>').appendTo($pop_up);
if(options.name != '') {
$pop_up.attr('id', options.name);
}
// Add the content - if url, load the page otherwise use the text
if (options.url != '') {
$inner.load(options.url);
} else {
var innerHTML = '';
if(options.title[0] === "<") { // assume formatting
innerHTML += options.title;
} else {
innerHTML += '<h2>' + options.title + '</h2>';
}
if(options.description[0] === "<") {
innerHTML += options.description;
} else {
innerHTML += '<p>' + options.description + '</p>';
}
$inner.html(innerHTML);
}
$close.click(function() {
// for example
return $pop_up.fadeOut().promise().then(function() {
$block_page.remove();
});
});
$(window).off('resize.popup').on('resize.popup', add_styles).trigger('resize.popup'); // prevent accumulation of resize handlers
// checkNeedScroll();
$pop_up.fadeIn();
}
EDIT 2
I think I have it!
In the custom_modal_box plugin, the code below causes a click handler to be appended to this:
return this.click(function(e) {
show_modal_box();
});
That's fine if the plugin is invoked just once on any particular element however in this game's code it is invoked on the same element, $('.win'), every time a game is completed.
To prevent an accumulation of click handlers on $('.win'), change that code to :
return this.off('click.popup').on('click.popup', function(e) {
show_modal_box();
});
I am having some trouble trying to store the url parameters of some dynamic links that I created with an ajax post response. The ajax post is working correctly and the name and subgenre vars are being properly filled from the ajax response. Now what I would like to happen is that a user clicks on one of the generated urls, the parameters inside of the urls, i.e. subgenre="blah", are going to be sent to a database and stored. The problem I am having is that a standard event click function will not work inside or outside of the document ready function.
$(document).ready(function() {
$.each(data, function() {
$('#artist-suggestions').append('<li>' + this.name + this.new + '</li>');
});
});
I then created an onclick function, as below, but I can not use the "this" query because it is outside of the document scope. I had to put the onclick function outside of the document ready function or else it would not work.
function artistGen(){
alert('dfdsf');
};
What am I missing here or what am I doing wrong?
You can pass these in the onclick function when you make each element.
$(document).ready(function() {
$.each(data, function() {
artist = this.name;
$('#artist-suggestions').append('<li>' + this.name + this.new + '</li>');
});
})
;
function artistGen(Blah1, Blah2){
saveData(Blah1, Blah2);
alert('dfdsf');
};
In jQuery for dynamic elements you can use the click event in this way
$('#artist-suggestions li').on('click', 'a', function() {
// do something
});
or you can continue with the way you did, by using a function but just add a parameter to that function
like
function artistGen(Artist){
// do something
};
You need to remove the artistGen() function from the scope of the .load()
$(window).load(function(){
$('#artist-suggestions').append('<li>jim new</li>');
});
function artistGen(){
alert('dfdsf');
}
JSFIDDLE DEMO
That's just how it is a function called in those event attributes have to be defined globally(or defined right there) not in any wrapper function. A better solution would be to attach event handlers.
$(document).ready(function() {
function artistGen(){
alert(this.href);
};
$.each(data, function() {
var $li = $('<li>' + this.name + this.new + '</li>');
$li.find('a').on('click', artistGen);
$('#artist-suggestions').append($li)
});
});
I'm trying to bind a hover event to some elements, walking through them with $.each, with the peculiarity that I want to pass a css classname as a parameter of the hover's handler functions, but it seems that the scope is not the one I'm expecting. I've tried to
$(document).ready(function () {
var $madewithLabels = $("#made-with .label");
// Binding
$madewithLabels.each(function (index) {
// get bootstrap css classname for the current element in the loop
var bsClass = getHoverClass($(this));
console.info("css class is: " + bsClass + " - " + typeof(bsClass));
$(this).hover(
function (bsClass) {
console.info(bsClass);
$(this).addClass(bsClass);
},
function (bsClass) {
console.info(bsClass);
$(this).removeClass(bsClass);
}
);
});
});
1st console.info: getHover() gets the right css class name (string) when the events are bound (on document ready)
2nd/3rd console.info: when hover's handler functions are executed bsClass is an object (I guess it's a jQuery one)
I've solved it this way:
$(document).ready(function () {
var $madewithLabels = $("#made-with .label");
// Binding
$madewithLabels.each(function (index) {
$(this).hover(
function () {
$(this).addClass(getHoverClass($(this)));
},
function () {
$(this).removeClass(getHoverClass($(this)));
}
);
});
});
But my questions are...
Is using $(this) the right solution?
Why when I pass a string variable to the handler functions I get an object when the function is called? is it because some type casting? is it because closure scope?
Thanks to the jQuery gurus answering!
What you're getting in the hover callback is an Event object, as mentioned by the docs:
handlerIn
Type: Function( Event eventObject )
A function to execute when the mouse pointer enters the element.
So in your first example change:
function (bsClass) {
To this:
function () {
So you keep using the original bsClass that you calculated before.
I'm currently writing an object oriented module which assigns callback to dynamically generated elements.
function Instant(containerID) {
this.var1 = 0;
this.var2 = 0;
this.containerID = containerID;
// and more variables...
};
And here containerID is the id of a DIV which is dynamically generated. I populate this DIV via Ajax Request which reads a file like the following:
<!-- content.html -->
<div class="general_container">
<div class="top_container">
<!-- plenty of divs, spans etc -->
</div>
<div class="tweet_section">
<!-- plenty of divs, spans etc -->
</div>
</div>
Now the important part is, I assign all callbacks like the following:
Instant.prototype.addCallbacks = function() {
$(this.containerID + " bar").click(function() {
$(this.containerID + " bar").foo();
});
$(this.containerID + " bar").click(function() {
$(this.containerID + " bar").foo();
});
$(this.containerID+ " bar").click(function(e) {
$(this.containerID + "bar, " + this.containerID+ " bar").foo();
});
});
As you see, I always have to put this.containerID before each selector to assign events. (Therefore, I make sure I'm selecting only one element) Now, my code is full of clutter as I have plenty of this.containerIDs. I don't know if there is a smarter method to make my code easy. Any help will be appreciated.
Here is a sample JSFiddle.
Note that this is not my real module, I just made it up to make it clear!
Then you shouldn't be using IDs. You should be using classes instead.
It would take long to edit your code, but here's a hint: Add a handler to the parent. Use event delegation, like .on(). Then have it listen for all children, now or future.
Create a separate java script file and put your add callbacks function in there and just pass the containerID. That way, you can re-use it later. However, looks like you cannot get rid of containterID since you will be needing that to do your add, subtract, save etc..
in your current file shown as above,
Instant.prototype.addCallbacks = createAddCallbacks(this.ContainerID);
create addCallbacks.js
function createAddCallbacks(containerId)
{
Instant.prototype.addCallbacks = function() {
$(containerId + " bar").click(function() {
$(containerId + " bar").foo();
});
$(containerId + " bar").click(function() {
$(containerId + " bar").foo();
});
$(containerId+ " bar").click(function(e) {
$(containerId + "bar, " + containerIdD+ " bar").foo();
});
});
}
Like #JosephTheDreamer said, use Event Delegation. (Jquery.fn.on)
Using event delegation you set one handler to multiple targets. It means just one handler in memory and dynamic event handlers set.
I made a demonstration modifying your code, take a look...
Instant.prototype.addCallbacks = function () {
var selfContainer = null, // DOMElement container
me = this; // Object reference
$('body').on("click", ".selection_container .btn-add", function () { //Using event delegation
selfContainer = $(this).parents(".general_container"); //set DOMElement
selfContainer.find("input[name=currentValue]").val(++me.instantValue);
});
$('body').on("click", ".selection_container .btn-subtract", function () {
selfContainer.find("input[name=currentValue]").val(--me.instantValue);
});
$('body').on("click", ".selection_container .btn-reset", function () {
me.instantValue = 0;
selfContainer.find('input[name=currentValue]').val(0);
});
$('body').on("click", ".selection_container .btn-save", function () {
me.savedValue = me.instantValue;
});
$('body').on("click", ".selection_container .btn-load", function () {
me.instantValue = me.savedValue;
selfContainer.find('input[name=currentValue]').val(me.savedValue);
});
};
Hope it helps...
So, I think I find a better method according to this post
I wanted to limit the scope of my selector.
Firstly, I'll create a jQuery instance variable
function Instant(containerID) {
this.var1 = 0;
this.var2 = 0;
this.container= $('#'+containerID);
// and more variables...
};
and adding a new prototype like this
Instant.prototype.$ = function(selector){
return this.container.find(selector);
};
I'll only use this.$(selector) function which is better.
I'm wondering how is possible to call a function with parameters inside a method.
I have 2 functions and i'd like to call function deleteCode() when clicked on list element which is created by addCode() function.
I'm sure the solution is really simple, but i just can't see it right now.
Many thanks!
function addCode(code) {
$('#codeList').append('<li class="codeList" onClick="deleteCode(code);">' + code + '</li>');
}
function deleteCode(code) {
$('#'+code).remove();
}
Do it unobtrusive and you're fine.
function addCode(code) {
$('#codeList').append($('<li>', {
'class': 'codeList',
'text': code,
'click': function(e) {
deleteCode(code);
}
}));
}
Ref.: $()
Create the <li> element via code rather than appending raw HTML.
function addCode(code) {
// Create the <li>
var newEl = document.createElement("li");
newEl.className = "codeList";
// Assign the click function via jquery's event helper.
$(newEl).click(function(code) {
// Call your deleteCode function and pass in the given parameter.
deleteCode(code);
});
// Append the new element to the codeList node.
$(#codeList).append(newEl);
}
You can try:
function addCode(code) {
$('#codeList').append('<li class="codeList" onClick="deleteCode(' + code + ');">'+code+'</li>');
}
You can do that like this:
function addCode(code) {
$('<li class="codeList">' + code + '</li>').click(function() {
deleteCode(code);
}).appendTo('#codeList');
}
function deleteCode(code) {
$('#'+code).remove();
}
...or more simply:
function addCode(code) {
$('<li class="codeList">' + code + '</li>').click(function() {
$('#'+code).remove();
}).appendTo('#codeList');
}
When using a library like jQuery (or even when not, frankly), there's virtually never any reason to use the old-style onclick attributes for setting up handlers. In the above, I've replaced it with the click function, which sets up a handler when the user clicks the element.
Note: Lazarus notes that your code is removing an element by id using the code value:
$('#' + code).remove();
...but that the code doesn't produce an element with that ID. I assume you've added that element with some other code elsewhere, and that the goal isn't to remove the li you've added with this code.
If you did want to remove that same li on click, no need for an ID at all:
function addCode(code) {
$('<li class="codeList">' + code + '</li>').click(function() {
$(this).remove(); // <== Changed here
}).appendTo('#codeList');
}