I have an element which I want to expand on click and then collapse on click outside and thus came up with the following code. However when I run this it will start to expand and then immediately collapse since both functions are called sequentially. I don't understand why and how to solve this.
jQuery(document).ready(function() {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
var fnSearch = {
expand : function() {
jQuery(element).animate({
width : expandWidth
});
jQuery(document).bind('click', fnSearch.collapse);
},
collapse : function() {
jQuery(element).animate({
width : defaultWidth
});
event.stopPropagation();
jQuery(document).unbind("click", fnSearch.collapse);
}
}
jQuery("#search-main").bind("click", fnSearch.expand);
});
You are having the problem because the #search-main click event is propagating to the document; i.e. first the #search-main click event triggers, then the document click event triggers. Click events do this by default. To stop this event propagation, you want to use http://api.jquery.com/event.stoppropagation/ in your expand function:
jQuery(document).ready(function() {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
var fnSearch = {
expand : function(event) { // add event parameter to function
// add this call:
event.stopPropagation();
jQuery(element).animate({
width : expandWidth
});
jQuery(document).bind('click', fnSearch.collapse);
},
collapse : function() {
jQuery(element).animate({
width : defaultWidth
});
jQuery(document).unbind("click", fnSearch.collapse);
}
}
jQuery("#search-main").bind("click", fnSearch.expand);
});
That said, Jason P's solution is better for what you want. It's more reliable and less messy, since you don't have to bind stuff to the document, which can easily become hard to track and cause conflicts with other code if you use that strategy habitually.
You could unbind the click event from the #search-main element after clicking, or stop the propagation of the event, but I would recommend binding to the blur and focus events instead:
http://jsfiddle.net/6Mxt9/
(function ($) {
$(document).ready(function () {
var element = jQuery("#search-main");
var defaultWidth = jQuery("#search-main").css('width');
var expandWidth = "200px";
$('#search-main').on('focus', function () {
$(element).animate({
width: expandWidth
});
}).on('blur', function () {
$(element).animate({
width: defaultWidth
});
});
});
})(jQuery);
That way, it will work even if the user tabs in or out of the field.
Related
I want to click on Item1, replace the label "Item1" with "Saved", then fade out the button after 500ms and place back the label "Item1" (saved in var currentText)
If I click the button multiple times it fires too many times. How can I prevent that?
$('body').on('click', ".item", function() {
var currentText = $(this).text();
$(this).text('Saved!').delay(500).fadeOut("fast", function() {
$(this).text(currentText).css('display', '');
});
});
This could be solved with a simple flag indicating that you are in the process of fading it out.
var isFadingOut = false;
$('body').on('click', ".item", function() {
if (isFadingOut) {
return;
}
isFadingOut = true;
var currentText = $(this).text();
$(this).text('Saved!').delay(500).fadeOut("fast", function() {
$(this).text(currentText).css('display', '');
isFadingOut = false;
});
});
Note: this solution works globally. So if you have multiple different buttons on screen that you want to be able to fade out simultaneously, this will not work. If that's the case, something more like what #Phiter wrote would be better.
I'd do something like this:
$('body').on('click', ".item", function() {
if ($(this).data('off')) return;
$(this).data('off', true);
var currentText = $(this).text();
$(this).text('Saved!').delay(500).fadeOut("fast", function() {
$(this).text(currentText).css('display', '');
$(this).data('off', false);
});
});
The function will not execute while the button has the off data. Kinda like Mike's answer but without the global variable.
Can use not(':animated'). The :animated pseudo selector is used internally by jQuery and is only active when an animation is in progress
$('body').on('click', ".item", function() {
var currentText = $(this).text();
$(this).not(':animated').text('Saved!').delay(500).fadeOut("fast", function() {
$(this).text(currentText).css('display', '');
});
});
I have a issue where the .click from the div where my cancel button is nested in triggers when I click the button. This causes both the slideUp and slideDown to trigger at the same time, which results in the div staying visible. I've tried adding a state to prevent the div from sliding down again, but this does not have the desired effect.
$(add).click(function () {
var inputDiv = Polymer.dom(root_root).querySelector("#inputDiv");
if(state == 0){
$(inputDiv).slideDown(300);
}
state = 1;
});
$(cancel).click(function () {
var inputDiv = Polymer.dom(root_root).querySelector("#inputDiv");
$(inputDiv).slideUp(300);
state = 0;
});
https://jsfiddle.net/kkdoneaj/2/
Does anyone know how to work around this issue?
Use Event.stopPropagation() to stop the click from bubbling from the #cancel button to the parent #add div:
$("#cancel").click(function(e) {
e.stopPropagation();
...
});
https://jsfiddle.net/kkdoneaj/3/
you should put stop
Stop the currently-running animation on the matched elements.
$(add).click(function () {
var inputDiv = Polymer.dom(root_root).querySelector("#inputDiv");
$(inputDiv).stop().slideDown(300);
});
$("#cancel").click(function (e) {
var inputDiv = Polymer.dom(root_root).querySelector("#inputDiv");
$(inputDiv).stop().slideUp(300);
e.stopPropagation();
});
Others have commented with stopPropagation(), but you can also not expand #inputDiv unless it's already visible, like so:
$(function(){
$("#add").click(function () {
if($("#inputDiv").css('display') == 'none'){
$("#inputDiv").slideDown(1000);
}
});
$("#cancel").click(function () {
$("#inputDiv").slideUp(1000);
});
});
I currently have a bootstrap popover holding a button. The popover shows only when the mouse is over a table's tr.
What I want to do is to be able to access the elements for that row, is this possible.
Popover code:
$('.popup').popover(
{
placement: 'top',
trigger: 'manual',
delay: { show: 350, hide: 100 },
html: true,
content: $('#shortcuts').html(),
title: "Quick Tasks"
}
).parent().delegate('#quickDeleteBtn', 'click', function() {
alert($(this).closest('tr').children('td').text()); // ???
});
var timer,
popover_parent;
function hidePopover(elem) {
$(elem).popover('hide');
}
$('.popup').hover(
function() {
var self = this;
clearTimeout(timer);
$('.popover').hide(); //Hide any open popovers on other elements.
popover_parent = self
//$('.popup').attr("data-content","WOOHOOOO!");
$(self).popover('show');
},
function() {
var self = this;
timer = setTimeout(function(){hidePopover(self)},250);
});
$(document).on({
mouseenter: function() {
clearTimeout(timer);
},
mouseleave: function() {
var self = this;
timer = setTimeout(function(){hidePopover(popover_parent)},250);
}
}, '.popover');
HTML:
<div class="hide" id="shortcuts">
Delete
</div>
javascript that implements popover on row:
rows += '<tr class="popup datarow" rel="popover">';
Does anyone know what I'm doing wrong here and how I am supposed to access the child elements of the tr I'm hovering over?
JSFiddle: http://jsfiddle.net/C5BjY/8/
For some reason I couldn't get closest() to work as it should. Using parent().parent() to get to the containing .popover divider, then using prev() to get the previous tr element seems to do the trick however.
Just change:
alert($(this).closest('tr').children('td').text());
To:
alert($(this).parent().parent().prev('tr').children('td').text());
JSFiddle example.
As a side note, as your Fiddle uses jQuery 1.10.1 you should change delegate() to on():
on('click', '#quickDeleteBtn', function(index) { ... });
Here I have fixed it.
You just have to pass the container option in which the popover element is added for the popover
$('.popup').each(function (index) {
console.log(index + ": " + $(this).text());
$(this).popover({
placement: 'top',
trigger: 'manual',
delay: {
show: 350,
hide: 100
},
html: true,
content: $('#shortcuts').html(),
title: "Quick Tasks",
container: '#' + this.id
});
});
In your button click alert, $(this) refers to the button itself. In the DOM hierarchy, the popover html is nowhere near your hovered tr.
Add a handler to the list item to store itself in a global variable and access that from the click event. See the forked fiddle here.
First we declare a global (at the very top):
var hovered;
Then we add a mouseover handler to the list item. Note that using 'on' means every newly generated list item will also receive this handler:
$('body').on('mouseover', '.popup', function() {
hovered = $(this);
});
Then we can alert the needed data from within the button click event:
alert(hovered.text());
See here JS Fiddle
by removing the delegate and using the id to find the button and attaching it to a click handler by making the popover makes it easier to track it
$(self).popover('show');
$('#quickDeleteBtn').click(function(){
alert($(self).text());
});
also note
$('#shortcuts').remove();
because you were using the button in the popover with the same ID in the #shortcuts we couldn't select it first, now we remove it we can
You already have the correct element in your code. Just reuse the popover_parent variable and you are all set :) FIDDLE
alert($(popover_parent).text());
Or you could do something around like this :
$('.popup').hover(
function () {
var self = this;
clearTimeout(timer);
$('.popover').hide(); //Hide any open popovers on other elements.
$('#quickDeleteBtn').data('target', '');
popover_parent = self;
//$('.popup').attr("data-content","WOOHOOOO!");
$('#quickDeleteBtn').data('target', $(self));
$(self).popover('show');
},
function () {
var self = this;
timer = setTimeout(function () {
$('#quickDeleteBtn').data('target', '');
hidePopover(self)
}, 250);
});
$(document).on({
mouseenter: function () {
clearTimeout(timer);
},
mouseleave: function () {
var self = this;
timer = setTimeout(function () {
$('#quickDeleteBtn').data('target', '');
hidePopover(popover_parent)
}, 250);
}
}, '.popover');
I just store the element clicked in your #quickDeleteBtn then use the link.
FIDDLE HERE
Overview:
I have a page which uses jquery.event.drag and jquery.event.drop.
I need to be able to drag and drop onto elements which are constantly being added to the dom, even after the drag has started.
Problem:
When the dragstart event fires it checks for available drop targets and adds them to the drag object.
The problem I have is I am adding drop targets dynamically, after the dragstart event has fired, and therefore the user cannot drop onto these dynamically added drop targets.
Example:
http://jsfiddle.net/blowsie/36AJq/
Question:
How can I update the drag to allow dropping on elements which have been added to the dom after drag has started?
You can use this snippet.
The important function is: $.event.special.drop.locate();
Tested on chrome/safari/firefox/ie9 and seems to work.
SEE DEMO
UPDATE
For overlapping events, see if following code works. I set it inside an anonymous function just to avoid any global variable.
Idea is to use currentTarget property of event to check if not the same element is triggering same event. I set an id on newdrop element just in purpose of test here.
SEE UPDATED DEMO
(function () {
var $body = $("body"),
newdrops = [],
currentTarget = {},
ondragstart = function () {
$(this).css('opacity', .75);
}, ondrag = function (ev, dd) {
$(this).css({
top: dd.offsetY,
left: dd.offsetX
});
}, ondragend = function () {
$(this).css('opacity', '');
for (var i = 0, z = newdrops.length; i < z; i++)
$(newdrops[i]).off('dropstart drop dropend').removeClass('tempdrop');
newdrops = [];
}, ondropstart = function (e) {
if (currentTarget.dropstart === e.currentTarget) return;
currentTarget.dropstart = e.currentTarget;
currentTarget.dropend = null;
console.log('start::' + e.currentTarget.id)
$(this).addClass("active");
}, ondrop = function () {
$(this).toggleClass("dropped");
}, ondropend = function (e) {
if (currentTarget.dropend === e.currentTarget) return;
currentTarget.dropend = e.currentTarget;
currentTarget.dropstart = null;
console.log('end::' + e.currentTarget.id)
$(this).removeClass("active");
};
$body.on("dragstart", ".drag", ondragstart)
.on("drag", ".drag", ondrag)
.on("dragend", ".drag", ondragend)
.on("dropstart", ".drop", ondropstart)
.on("drop", ".drop", ondrop)
.on("dropend", ".drop", ondropend);
var cnt = 0;
setInterval(function () {
var dataDroppables = $body.data('dragdata')['interactions'] ? $body.data('dragdata')['interactions'][0]['droppable'] : [];
var $newDrop = $('<div class="drop tempdrop" id="' + cnt + '">Drop</div>');
cnt++;
$("#dropWrap").append($newDrop);
var offset = $newDrop.offset();
var dropdata = {
active: [],
anyactive: 0,
elem: $newDrop[0],
index: $('.drop').length,
location: {
bottom: offset.top + $newDrop.height(),
elem: $newDrop[0],
height: $newDrop.height(),
left: offset.left,
right: offset.left + $newDrop.width,
top: offset.top,
width: $newDrop.width
},
related: 0,
winner: 0
};
$newDrop.data('dropdata', dropdata);
dataDroppables.push($newDrop[0]);
$newDrop.on("dropstart", ondropstart)
.on("drop", ondrop)
.on("dropend", ondropend);
$.event.special.drop.locate($newDrop[0], dropdata.index);
newdrops.push($newDrop[0]);
}, 1000);
})();
I wasn't able to get this working using jquery.event.drag and jquery.event.drop, but I did make it work with the native HTML5 events:
http://jsfiddle.net/R2B8V/1/
The solution was to bind the events on the drop targets within a function and call that to update the bindings. I suspect you could get this working with jquery.event.drag and jquery.event.drop using a similar principal. If I can get those working I will update my answer.
Here is the JS:
$(function() {
var bind_targets = function() {
$(".drop").on({
dragenter: function() {
$(this).addClass("active");
return true;
},
dragleave: function() {
$(this).removeClass("active");
},
drop: function() {
$(this).toggleClass("dropped");
}
});
};
$("div[draggable]").on({
dragstart: function(evt) {
evt.originalEvent.dataTransfer.setData('Text', 'data');
},
dragend: function(evt) {
$('.active.drop').removeClass('active');
}
});
setInterval(function () {
$("#dropWrap").append('<div class="drop">Drop</div>');
// Do something here to update the dd.available
bind_targets();
}, 1000)
});
You can't. On dragstart, possible drop zones are calculated from the DOM, and can't be edited until dragend. Even constantly rebinding the .on() (Demo: http://jsfiddle.net/36AJq/84/) will not provide the desired effect.
I solved the issue a little differently. (Demo: http://jsfiddle.net/36AJq/87/)
Start with every <div> in the HTML.
Apply opacity: 0 to make it invisible, and width: 0 to keep it from getting a dropend when hidden.
Use setInterval to show the next hidden div ($('.drop:not(.visible)').first()) each 1000ms.
JS:
$("body")
.on("dragstart", ".drag", function () {
$(this).css('opacity', .75);
})
.on("drag", ".drag", function (ev, dd) {
$(this).css({
top: dd.offsetY,
left: dd.offsetX
});
})
.on("dragend", ".drag", function () {
$(this).css('opacity', '');
})
.on("dropstart", ".drop", function () {
$(this).addClass("active");
})
.on("drop", ".drop", function () {
$(this).toggleClass("dropped");
})
.on("dropend", ".drop", function () {
$(this).removeClass("active");
});
setInterval(function () {
$('.drop:not(.visible)').first()
.addClass('visible').removeClass('hidden');
}, 1000)
Enable the refreshPositions option.
Why not place all the divs into the page and set their visibility to hidden? Then use setInterval() to change each one's visibility every second.
So, i have some animation actions, this is for my login panel box:
$('.top_menu_login_button').live('click', function(){
$('#login_box').animate({"margin-top": "+=320px"}, "slow");
});
$('.login_pin').live('click', function(){
$('#login_box').animate({"margin-top": "-=320px"}, "slow");
});
now i need to add some hiding action after click on body so i do this:
var mouse_is_inside = false;
$('#login_box').hover(function () {
mouse_is_inside = true;
}, function () {
mouse_is_inside = false;
});
for stop hiding this element on body click, and this for body click outside by login-box
$("body").mouseup(function () {
if (!mouse_is_inside) {
var login_box = $('#login_box');
if (login_box.css('margin-top','0')){
login_box.stop().animate({"margin-top": "-=320px"}, "slow");
}
}
});
Everything is fine but this panel animates after each body click, how to stop this and execute only one time? Depend on this panel is visible or not?
You'd normally do this sort of thing by checking if the click occured inside the element or not, not by using mousemove events to set globals :
$(document).on('click', function(e) {
if ( !$(e.target).closest('#login_box').length ) { //not inside
var login_box = $('#login_box');
if ( parseInt(login_box.css('margin-top'),10) === 0){
login_box.stop(true, true).animate({"margin-top": "-=320px"}, "slow");
}
}
});
And live() is deprecated, you should be using on().