I am having some trouble removing an element created by jQuery in the same function. Here is what is happening:
I have created a jQuery function (toggleClick) that opens up a side panel and then creates an overlay element that is applied to the rest of the page (excluding the panel).
What is supposed to happen is after the user clicks on the overlay it will close the panel and also get rid of the overlay element. Am I doing something wrong here?
jQuery Code:
jQuery(document).ready(function(){
/* DEFINE TOGGLECLICK */
$.fn.toggleClick=function(){
var functions=arguments
return this.click(function(){
var iteration=$(this).data('iteration')||0
functions[iteration].apply(this,arguments)
iteration= (iteration+1) %functions.length
jQuery(this).data('iteration',iteration)
})
}
/* DEFINE APANEL ID */
var aPanel = "#aPanel";
/* DEFINE APANEL FUNCTIONALITY */
jQuery('#apanel-fire,#aPanelOverlay').toggleClick(function() {
jQuery("body").addClass("active");
jQuery(aPanel).addClass("active");
jQuery("<div id='aPanelOverlay'></div>").appendTo(".page");
}, function() {
jQuery("body").removeClass("active");
jQuery(aPanel).removeClass("active");
jQuery("#aPanelOverlay").remove();
});
});
The /* DEFINE APANEL FUNCTIONALITY */ is where I am having the issue. Everything else above is defining the toggleClick function and the aPanel variable ID.
You can see a live version of the issue I am having below by clicking on the Menu button located in the top left of the screen.
Live Version:
http://iamaaron.com/alpha/
Thank you so much!
First of all when you call jQuery('#apanel-fire,#aPanelOverlay').toggleClick the overlay does not exist, as it is attached later. So it has no click handler and does not react to any click.
Second (which does not really matter anymore ;) return this.click(function(){ would attach a click handler to each element. jQuery(this).data('iteration',iteration) would store the iteration on #apanel-fire on the first click. When you would now click on the overlay, its iteration would still be 0. So the first function would be called again.
Update: a very simple solution
function showOverlay() {
jQuery("body").addClass("active");
jQuery(aPanel).addClass("active");
jQuery("<div id='aPanelOverlay'></div>").appendTo(".page").click(hideOverlay);
}
function hideOverlay() {
jQuery("body").removeClass("active");
jQuery(aPanel).removeClass("active");
jQuery("#aPanelOverlay").remove();
}
jQuery('#apanel-fire').click(showOverlay);
Related
My code has a button that changes some CSS via adding and removing classes. I have the JS working fine to do this. The first click adds the class, clicking again removes it and so on around it goes, nothing unusual there.
However, I've also incorporated a function that removes the classes if the browser window is resized at all. My issue is, if I then go back to press the button again after resizing the window, it thinks it should be doing the second click (almost reversing the function) and removes the classes (even though they've already been removed by the resizing), whereas I need the button function to almost reset and have it think the button hasn't been clicked yet after the resizing, so the process can be started from the beginning.
I really hope this makes sense, because its been driving me around the bend, and nothing I've tried will make it work how I would like it to.
Thank you for any help!
Heres the code:
JS/jQuery
{
/* Button Function to add and remove classes. */
$("#nav-icon").click(function () {
var clicks = $(this).data("clicks");
if (!clicks) {
$(".logo-container").addClass("border-change-image");
$(".container-fluid").addClass("border-change-header");
} else {
$(".logo-container").removeClass("border-change-image");
$(".container-fluid").removeClass("border-change-header");
}
$(this).data("clicks", !clicks);
});
/* Window Resizing Function */
$(window).on("resize", function () {
var size = $(this).on("resize");
if (size) {
$(".logo-container").removeClass("border-change-image");
$(".container-fluid").removeClass("border-change-header");
} else {
}
});
}
Removed the variable storing the data and just "asked" if one of the div's had one of the classes then add or remove, based on that. Works like a charm.
JS
$("#nav-icon").click(function () {
if ($(".logo-container").hasClass("border-change-image")) {
$(".logo-container").removeClass("border-change-image");
$(".container-fluid").removeClass("border-change-header");
} else {
$(".logo-container").addClass("border-change-image");
$(".container-fluid").addClass("border-change-header");
}
});
I hit a problem with the onclick function when i add divs with ids like "n_block"+(1-~). When I use the jquery zoom function on the objects to make them smaller or bigger onClick doesn't work anymore. I'm not really good at programming so the code might be kind of confusing.
Heres the code i use for the onClick of items:
$(document).on("click",function (e, ui){
//When the document gets clicked, check if one of the items was clicked.
if($(e.target).is($("#n_block" + cloneCount1)) || $(e.target).is($("#n_block" + cloneCount1+ " span"))){
//Set current item.
var item = $("#n_block" + cloneCount1);
//Set style to current item.
item.css("border-color", "Black");
item.css("border-width","2px");
item.css("background-color", "floralwhite");
jsPlumb.repaintEverything();
//Check if key Delete was pressed while item selected & delete that item with his children.
$('html').keydown(function(e){
if(item.css("border-width")=="2px"){
if(e.keyCode == 46) {
/* Prevents line bugging*/
jsPlumb.detachEveryConnection();
jsPlumb.deleteEveryEndpoint();
var razred = getClass(item, "b_"),
id = item.prop("id");
item.remove();
if(razred == "b_2"){
$(".ovoj."+id).remove();
}
else if (razred == "b_4"){
$(".ovojLoop."+id).remove();
$(".empty_block_c."+id).remove();
}
if ( $('.objects').find('div').length == 2) {
$(".objects").empty();
$(".objects").append('<div class="b_s" id="start_block">START</div><p id="start_text">Insert symbols here!</p><div class="b_s" id="end_block">STOP</div> ');
}else{
/* Connects objects together with line. ****/
povezi(cloneCount, tip_crte, ".objects");
}
}
jsPlumb.repaintEverything();
}
});
}
// If item is not clicked set this css to the current item.
else{
$("#n_block" + cloneCount1).css("border-width","1px");
jsPlumb.repaintEverything();
}
});
And heres the zoom code for zooming in when button is clicked:
var currentZoom = 1.0;
$(".zoomin").click(function (){
//Detaches the connections from item to item.
jsPlumb.detachEveryConnection();
jsPlumb.deleteEveryEndpoint();
//Prevents spamming of button, animates the objects
$(".project").stop().animate({ "zoom": currentZoom += .1}, "slow", function() {
if(!$(".objects").children().is($("p"))){
povezi(cloneCount, tip_crte, ".objects");
}
});
});
Use event delegation for binding events to dynamically added elements.
$(document).on('click', ".zoomin", function (){
//Your code.
});
When you use normal .click() to bind event to an element, then that even gets bound to only those elements which exist in the DOM at the moment of the execution of code. Using event delegation, you can tell jQuery that we need to add the handler to every '.zoomin' element which comes inside a particular element no matter when it is added.
The solution depends when exactly is the script which tries to bind the events are executed.
For Eg: Lets assume this script is in document ready function of jquery
$(document).ready(function(){
$(".zoomin").click(function (){
//your logic here
});
});
Here this script is executed when the page HTML is completed loading into the browser. Now when the script executes it tries to find a element with the class zoomin and if found it will add a event to that element and move on. If the element is not found the script just moves on. So we should actually take care of when the script is executed and is the intended element available at that particular instant of time. If the element is not yet available in the HTML (element might come in later dynamically using jquery) we have 2 options to bind event to the element.
1) Execute the script when the element is being added into the HTML: Lets say I have a event which brings up a pop up with some image. Now I want to zoomin and zoomout the image. Since the image in the popup is added dynamically and I have control of when its being added, I can do this.
$(document).ready(function(){
$('.ViewImage').on('click',function(){
// some code is executed which brings up the popup
// now we know that the image is added into html we can now run the script
$(".zoomin").click(function (){
//your logic here
});
});
});
2) We have no Clue/ Control when the element is added into HTML but still want to bind a event to it: This is scenario where we have no control on when the element is being added or not sure where it is being added from (might be from some external plugin used etc) or not having control at all on the element which is added. Thats when we use this syntax as suggested by #Rejith R Krishnan
$(document).on('click', ".zoomin", function (){
//Your code.
});
This will work on all the elements which are in the HTML at the time of execution of the script and on the elements which will be added in the future with the class name zoomin. So this script can be placed inside/ outside of jquery document ready event
I think that I currently write pretty good javascript, but am trying to move to a more Object Oriented approach. I am just starting with this so forgive my noob-ness. I was moving some of my functions over to objects and ran into this issue. Previously, I had an accordion function that worked like this:
jQuery(document).ready(function ($){
var accordionTrigger = $('.accordion-title');
function toggleAccordion() {
// Set a variable for the accordion content
var accordionContent = $('.accordion-container .accordion-content p');
// Slide up any open content
accordionContent.slideUp();
// Remove any active classes
accordionTrigger.removeClass("active");
// If the sibling content is hidden
if(!$(this).siblings().is(":visible")) {
// slide it down
$(this).siblings().slideDown();
// add a class to the title so we can style the active state and change the svg
$(this).addClass("active");
}
}
accordionTrigger.on("click", toggleAccordion);
});
I have moved this over to an Object in my new set-up like this:
Accordion = {
accordionContent: '.accordion-container .accordion-content p',
accordionTrigger: '.accordion-title',
init: function() {
jQuery(this.accordionTrigger).click(this.toggleAccordion.bind(this));
},
toggleAccordion: function() {
// Slide up any open content
jQuery(this.accordionContent).slideUp();
// Remove any active classes
jQuery(this.accordionTrigger).removeClass("active");
// If the sibling content is hidden
if(!jQuery(this.accordionTrigger).siblings().is(":visible")) {
// slide it down
jQuery(this.accordionTrigger).siblings().slideDown();
// add a class to the title so we can style the active state and change the svg
jQuery(this.accordionTrigger).addClass("active");
}
}
}
jQuery(document).ready(function ($){
Accordion.init();
});
The issue that I'm running into is with the way that 'this' works in Object Oriented Javascript. In the original setup, I was able to use 'this' to reference the accordion content that was clicked. I do not have access to that with the Object Oriented method. Can someone please help me out with?
You can use event.target to refer to the element that triggered the event, or event.currentTarget to refer to the element the handler was bound to, which is equivalent to using this.
toggleAccordion: function(event) {
// Slide up any open content
jQuery(this.accordionContent).slideUp();
// Remove any active classes
jQuery(this.accordionTrigger).removeClass("active");
// If the sibling content is hidden
if(!jQuery(event.currentTarget).siblings().is(":visible")) {
// slide it down
jQuery(event.currentTarget).siblings().slideDown();
// add a class to the title so we can style the active state and change the svg
jQuery(event.currentTarget).addClass("active");
}
}
Learn more about event handling with jQuery.
The problem is not how this works on OOP JavaScript because this is always referencing the caller object.
You can learn more about this with You Don't Know JS: this & Object Prototypes.
You can indeed use event.target and event.currentTarget as #Felix says, but eventually you will get into trouble if you don't understand how to use this properly or at least understand how it works.
An alternative approach to #FelixKling solution, removing .bind() , using event.data to have access to both jQuery(this) and Accordion object within accordionTrigger handler
var Accordion = {
accordionContent: '.accordion-container .accordion-content p',
accordionTrigger: 'div',
init: function() {
// set `event.data` to `this`:`Accordion` at first parameter to `.click()`
jQuery(this.accordionTrigger).click(this, this.toggleAccordion);
},
toggleAccordion: function(e) {
// Slide up any open content
// jQuery(e.data.accordionContent).slideUp();
// Remove any active classes
// jQuery(e.data.accordionTrigger).removeClass("active");
// If the sibling content is hidden
// `this`:`div`
if (jQuery(this).is(e.data.accordionTrigger)) {
console.log(this, e.data, $(e.data.accordionTrigger))
}
}
}
Accordion.init()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div>
click
</div>
Within toggleAccordion(), you can still use $(this) to get the accordion title element that has been clicked on. Hopefully then you can get the appropriate accordion content.
First bind it like how PHPglue mentioned:
jQuery(this.accordionTrigger).click(this.toggleAccordion);
Then you can do $(this) to get the accordionTitle:
toggleAccordion: function() {
var $accordionTitle = $(this);
}
Basically when you bind it like that and the click is triggered, the scope changes and "this" is no longer "Accordion".
If you need to access accordionContent and accordionTrigger, you can either pass it into the function toggleAccordion or use Accordion.accordionContent and Accordion.accordionTrigger.
Additional source: http://api.jquery.com/click/#click-eventData-handler
I'm using JQuery mobile framework here, so i've got two 'pages' which in reality both exist in one HTML document. You can navigate between the two via the hamburger menu icon on the top left.
There is an events page. each events has an 'add to favourites' button. when clicked the event populates the favourites page. I'm doing this by cloning the div class.
var add = $('.add-to-fav');
add.on('click', function(){
//change button status
if ($(this).text() == "Add to favourites")
{
$(this).text("Remove from favourites");
$(this).closest(".event-block").clone().appendTo('#fav-list');
} else {
$(this).text("Add to favourites");
//get this instance of event block and remove from #fav-list
}
});
My issue comes with trying to remove (or unfavourite) the event by clicking again on the same button. I want this to be achievable from both the events page and the favourites page.
What's the most elegant way to achieve this?
here's a codepen - i've tried to remove as much unrelated code as possible
http://codepen.io/MartinBort/pen/EajBXB/
First of all, you are using the same panel for both pages with same ID. Either use an external panel or give each one a unique ID. If you plan to use an external panel, place it outside any page and initialize it manually.
$(function () {
$("#menu-panel").panel().enhanceWithin();
});
To add listeners, use pagecreate it fires once per page. This way, you can assign different functions for buttons based on containing page. Use event.target to target buttons .add-fav within created page not other ones in DOM.
You can remove "Event" from favourites page from public-events page by retrieving its' header's text since you aren't using IDs' or classes to differentiate between events.
$(document).on("pagecreate", "#public-events", function (event) {
$('.add-to-fav', event.target).on('click', function () {
if ($(this).text() == "Add to favourites") {
$(this).text("Remove from favourites");
$(this).closest(".event-block").clone().appendTo('#fav-list');
} else {
$(this).text("Add to favourites");
/* retrieve event title */
var eventTitle = $(this).closest(".event-block").find("h2").text();
/* loops through event blocks in favourite page */
$('#favourites #fav-list .event-block h2').each(function () {
if ($(this).text() == eventTitle) {
/* remove matching event */
$(this).closest(".event-block").remove();
}
});
}
});
}).on("pagecreate", "#favourites", function (event) {
$('.add-to-fav', event.target).on('click', function () {
$(this).closest(".event-block").remove();
});
});
Demo
I'm using jsPlumb. My current functionality lets me create a .project div that can then have .task divs inside it. The .project div has 3 clickable buttons which all work using jQuery and the .tasks inside the .project have a single close button which also works.
As can we seen here: http://jsfiddle.net/9yej6/3/
(click the add project button then click on the green project and try to click on the X near some task - an alert should pop up)
However, whenever I try to make the .tasks a makeTarget/makeSource using jsPlumb it surpasses (probably not the best word) any other event done by jQuery. That is when I click on the X icon of the .task it instead acts as if I click on the .task itself and tries to create jsPlumb's bond.
As can be seen here: http://jsfiddle.net/9yej6/4/
So the following line no longer works (note I'm using the on() function since the .project/.task divs are dynamically created):
$("#container").on('click','.task .close',function(e) {
alert('a task`s add was clicked');
});
Initially the addTask() function was (which worked, but you can't add jsPlumb bonds):
function addTask(parentId, index) {
var newState = $('<div>').attr('id', 'state' + index).addClass('task');
var close = $('<div>').addClass('close');
newState.append(close);
var title = $('<div>').addClass('title').text('task ' + index);;
newState.append(title);
$(parentId).append(newState);
}
But when I add the makeTarget()/makeSource() calls to it, it seems to surpass any other jQuery event handling. Where my new addTask() function becomes:
function addTask(parentId, index) {
var newState = $('<div>').attr('id', 'state' + index).addClass('task');
var close = $('<div>').addClass('close');
newState.append(close);
var title = $('<div>').addClass('title').text('task ' + index);;
newState.append(title);
$(parentId).append(newState);
jsPlumb.makeTarget(newState, {
anchor: 'Continuous'
});
jsPlumb.makeSource(newState, {
anchor: 'Continuous'
});
}
You can also use the filter parameter to specify what element to be included for the object drag.
See my complete answer here.
http://jsplumbtoolkit.com/doc/connections.html#sourcefilter
jsPlumb.makeSource("foo", {
filter:":not(a)"
});
Above means, don't interfere with operations related to a (anchor tag).
As mentioned,
$("#container").on('click','.task .close',function(e) {
alert('a task`s add was clicked');
});
This code doesn't work becasue you have made the '.task' element as either target or source part of jsPlumb hence the mouse events will be handled by jsPlumb which prevents the default event handling(jQuery or pure JS) of those elements.
In such case you need to create a small rectangle DIV(refer image) from where the user can drag the connection instead of an entire DIV.