multiple firing on element fires jquery multiple times - javascript

I've three icons on my page. Each icon clicked, ajax called and contented fetched and appended to another div respectively. The problem I face now, if an icon fired multiple time continuously, it fires ajax for the no of time it's clicked.
Noticing this problem, I check, if the request pass in property called sweep. If it does I would empty the div and append the content; this is to avoid duplicate data being appended. But now when the icons pressed multiple times, it appends more than once the same data.I can't find what causing this to happen. For some reason, I had to use append and not html to put in the content.
I thought can overcome this by event.stopPropagation and event.stopImmediatePropagation but they're of no use. Please check my code below and help me to fix it...thanks..
screen cast of the problem:
demo
code:
//links in the footer clicked
$('body').off('click.footer').on('click.footer', '[data-footer] ul li a', function(event){
event.stopImmediatePropagation();
event.preventDefault();
event.stopPropagation();
var layout = $(this).data('ck-tab');
$(this).trigger('ck.chat.layout', [{'layout':layout}]);
if(layout == 'list'){
ckit.render({"layout":'getConversations', "sweep": 1, "limit":localStorage.getItem("cListlimit"), "userId": localStorage.getItem("ckuser")});
}else if(layout == 'contact'){
ckit.render({"layout":'getContacts', "sweep": 1, "limit":localStorage.getItem("cntlimit"), "userId": localStorage.getItem("ckuser")});
}else if(layout == 'setting'){
//ckit.render({"layout":'getSettings', "sweep": 1});
ckit.setting();
}
});
I check if data already appended to the div this way. But even this shows the data multiple times..
if($.inArray(data[i].id, temp)<0) {
//add to array
temp.push(data[i].id);
}
function to display content:
function display(value, arg){
var wrapper = $('[data-body-content]');
if(arg[0]['sweep'] == 1){
temp = [];
wrapper.empty();
}
setTimeout(function(){
for(var i=0; i < counter;i++){
if($.inArray(data[i].id, temp)<0) {
//add to array
temp.push(data[i].id);
}
}//end for
console.debug(temp);
wrapper.append(temp);
}, 300);
}

Related

keydown event triggered only once

The following script is aimed to run on facebook.com's conversations page (the page in which a user can see all its conversations).
The script's purpose is to automize the "delete conversation" process which naturally includes 4 clicks and can be tiresome and time wasting when you have hundreds of conversations --- deletion will be done from keyboard by hitting the "D" key.
I run the script with Greasemonkey.
The script is comprised of 4 main segments:
Listen to all "D" key hitting events.
Click the link that opens the modal with the "Delete" option (the small chainwheel).
Clink the "Delete" link in that modal (it will open a second modal "delete confirmation").
Click the new "Delete" link in that modal, to confirm conversation deletion.
My script
document.addEventListener('keydown', (k)=>{
if ( k.keyCode === 68 ) {
console.log('keydown: D');
return dC();
}
});
let dC = ()=>{
document.querySelector('._5blh._4-0h').click();
document.querySelector('.uiContextualLayer > [id^="js"] > div > ul > li:nth-child(4)').click();
setTimeout(()=>{ document.querySelector('._3quh._30yy._2t_._3ay_._5ixy').click(); }, 500);
};
As a beginner, I tried put parts of the code in functions, I tried iterating with for instead forEach(), I tried using return dC() or return false under the dC() call. All of these yielded the same results so I walked in circles not understanding (or denying) a deeper logical error which I sorely miss.
Reproducing
Install as Greasemonkey script, (match as https:www.facebook.com/* just for the test), go to conversations page and hit "D".
My question
Why the event is listened only once? That is, why clicking D once will case the script to work but any further clicks will do nothing?
I will have to refresh the page for that to reuse the script and that's not the intended behavior.
Note: I would prefer a vanilla solution.
As written, function dC(), it does 4 actions:
The problem was in action #3:
Seemingly, every click on 'settings' menu (action #2), a new 'settings-menu' instance is created (new object added to the DOM), thus this line won't work:
document.querySelector('.uiContextualLayer > [id^="js"] > div > ul > li:nth-child(4)').click();
On the one hand, querySelector returns the first element that answers the given pattern.
On the other hand, Facebook creates a new '.uiContextualLayer' each time the settings button is clicked (target the menu chainwheel link and stretch up your devtool window to note the new element added).
Hence, what we do is to check all chainwheel elements after and then work with the newest (last) element each time:
let menu = document.querySelectorAll('.uiContextualLayer._5v-0._53il');menu = menu[menu.length-1];
Here is the final code.
(I added few more timeouts to make sure drawing the UI is finished)
let dC = ()=>
{
// clicking the 'settings'
document.querySelector('._5blh._4-0h').click();
setTimeout(() => {
// taking the last instance of 'menu popup':
let menu = document.querySelectorAll('.uiContextualLayer._5v-0._53il');
menu = menu[menu.length-1];
// finding 'delete' button inside the menu popup
let lis = menu.querySelectorAll('ul > li');
let target = null;
for (let i=0;!target && i<lis.length;++i)
{
let span = lis[i].querySelector('a span span');
if (!span) continue;
if (span.innerHTML.contains('Delete'))
target = lis[i];
}
if (!target) {console.log('cannot find delete btn'); return;}
// clicking 'delete' button in menu
setTimeout(() => {
target.click();
setTimeout(()=>{
// clicking delete in modal
document.querySelector('._3quh._30yy._2t_._3ay_._5ixy').click();
}, 500);
}, 10);
},10);
};
Push the key values into arrays and unset these arrays after the event was over.
// Create two empty arrays
var map = [];
var down = [];
$(document).on('keydown', 'body', function(e) {
// Check whether the map having any key values
if (!map[e.which]) {
// Set the keydown value to down array
down.push(e.which);
if (down[0] === 68) {
// Events to be done
}
}
map[e.which] = true;
// Once the key-down pressed and event done ,the keyup unset the array while key up
}).keyup(function(e) {
map[e.which] = false;
// unset(down,e.which);
down = [];
e.which = [];
});

Event listener fails to attach or remove in some contexts

I've created a script that attaches an event listener to a collection of pictures by default. When the elements are clicked, the listener swaps out for another event that changes the image source and pushes the id of the element to an array, and that reverses if you click on the swapped image (the source changes back and the last element in the array is removed). There is a button to "clear" all of the images by setting the default source and resetting the event listener, but it doesn't fire reliably and sometimes fires with a delay, causing only the last element in a series to be collected.
TL;DR: An event fires very unreliably for no discernible reason, and I'd love to know why this is happening and how I should fix it. The JSFiddle and published version are available below.
I've uploaded the current version here, and you can trip the error by selecting multiple tables, pressing "Cancel", and selecting those buttons again. Normally the error starts on the second or third pass.
I've also got a fiddle.
The layout will be a bit wacky on desktops and laptops since it was designed for phone screens, but you'll be able to see the issue and inspect the code so that shouldn't be a problem.
Code blocks:
Unset all the selected tables:
function tableClear() {
//alert(document.getElementsByClassName('eatPlace')[tableResEnum].src);
//numResTables = document.getElementsByClassName('eatPlace').src.length;
tableArrayLength = tableArray.length - 1;
for (tableResEnum = 0; tableResEnum <= tableArrayLength; tableResEnum += 1) {
tableSrces = tableArray[tableResEnum].src;
//alert(tableSrcTapped);
if (tableSrces === tableSrcTapped) {
tableArray[tableResEnum].removeEventListener('click', tableUntap);
tableArray[tableResEnum].addEventListener('click', tableTap);
tableArray[tableResEnum].src = window.location + 'resources/tableBase.svg';
} /*else if () {
}*/
}
resTableArray.splice(0, resTableArray.length);
}
Set/Unset a particular table:
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'resources/tableBase.svg');
resTableArray.shift(this);
};
tableTap = function () {
$(this).unbind('click', tableTap);
$(this).bind('click', tableUntap);
this.setAttribute('src', 'resources/tableTapped.svg');
resTableArray.push($(this).attr('id'));
};
Convert the elements within the 'eatPlace' class to an array:
$('.eatPlace').bind('click', tableTap);
tableList = document.getElementsByClassName('eatPlace');
tableArray = Array.prototype.slice.call(tableList);
Table instantiation:
for (tableEnum = 1; tableEnum <= tableNum; tableEnum += 1) {
tableImg = document.createElement('IMG');
tableImg.setAttribute('src', 'resources/tableBase.svg');
tableImg.setAttribute('id', 'table' + tableEnum);
tableImg.setAttribute('class', 'eatPlace');
tableImg.setAttribute('width', '15%');
tableImg.setAttribute('height', '15%');
$('#tableBox').append(tableImg, tableEnum);
if (tableEnum % 4 === 0) {
$('#tableBox').append("\n");
}
if (tableEnum === tableNum) {
$('#tableBox').append("<div id='subbles' class='ajaxButton'>Next</div>");
$('#tableBox').append("<div id='cazzles' class='ajaxButton'>Cancel</div>");
}
}
First mistake is in tapping and untapping tables.
When you push a Table to your array, your pushing its ID.
resTableArray.push($(this).attr('id'));
It will add id's of elements, depending on the order of user clicking the tables.
While untapping its always removing the first table.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
resTableArray.shift(this);
So, when user clicks tables 1, 2, 3. And unclicks 3, the shift will remove table 1.
Lets fix this by removing untapped table
tableUntap = function () {
$(this).unbind('click', tableUntap);
$(this).bind('click', tableTap);
this.setAttribute('src', 'http://imgur.com/a7J8OJ5.png');
var elementID = $(this).attr('id');
var elementIndex = resTableArray.indexOf(elementID);
resTableArray.splice(elementIndex, 1);
};
So you were missing some tables after untapping.
Well lets fix tableClear,
You have a array with tapped tables, but you are searching in main array.
function tableClear() {
tableLen = resTableArray.length;
for (var i = 0; i < tableLen; i++) {
var idString = "#" + resTableArray[i];
var $element = $(idString);
$element.unbind('click', tableUntap);
$element.bind('click', tableTap);
$element.attr("src", 'http://imgur.com/a7J8OJ5.png');
}
resTableArray = [];
}
Im searching only tapped tables, and then just untap them and remove handlers.
fiddle: http://jsfiddle.net/r9ewnxzs/
Your mistake was to wrongly remove at untapping elements.

Moving multiple JQueryUI sliders in the DOM while keeping functionality & values

I have a page with a large number of JQueryUI sliders, in div .origin. Each slider has an associated checkbox. If the checkbox is checked, the slider should be moved- with all values intact- to div .favourites. If the checkbox is unchecked, the slider should be moved back to .origin.
Here's a JSFiddle of my attempts so far.
Here's my function to detect the value of the checkbox and make DOM adjustments accordingly:
$('[type="checkbox"]').click(function () {
var check = $(this),
checked = $(check).prop("checked"),
num = $(check).attr("id").split("-")[1],
parent = $("#food-" + num),
parentContent = $(parent).clone(),
slider = "#" + $(parent).find('.slider').attr("id"),
favelist = $(".favourites .content");
alert(checked)
//remove the original
$(parent).remove();
if (checked === true) {
//add to favourites
$(favelist).append(parentContent);
} else {
//add to origin
$(favelist).append(parentContent);
}
//reinitialise slider
loadSlider(slider)
});
A number of things aren't working:
1) First and most significantly, after the slider and it's parent elements have been moved into the .origin div, clicks on the checkbox no longer register. The alert(checked) doesn't fire, and the else statement doesn't run. Why would a checkbox no longer register a click event after being moved in the DOM?
2) Secondly, the moved sliders have the correct value displayed on the highlight div, but the dragger-handles go back to the starting position. How can I re-initialise the slider with the dragger handle in the correct position?
--
UPDATE- JSFIDDLE
OK, first issue is resolved, thanks to the hint provided by Chad, below. I've rewritten the click event into two functions, so that the click event can be re-instantiated after the elements are re-created in the DOM:
//Listen for checkbox to be checked
function checkboxChecked() {
$('[type="checkbox"]').click(function () {
var checkbox = $(this);
moveItem(checkbox);
});
}
//Create/Destroy items when clicked
function moveItem(checkbox) {
var check = $(checkbox),
checked = $(check).prop("checked"),
num = $(check).attr("id").split("-")[1],
parent = $("#food-" + num),
parentContent = $(parent).clone(),
slider = "#" + $(parent).find('.slider').attr("id"),
favelist = $(".favourites .content"),
origin = $(".all .content");
$(parent).remove();
if (checked === true) {
$(favelist).append(parentContent);
} else {
$(origin).append(parentContent);
}
loadSlider(slider);
checkboxChecked();
}
//initialise checkbox move function
checkboxChecked();
Am still trying to solve the mystery of the handle positions being re-set to 0 despite the slider retaining it's value, as shown on the highlighter track.

how do i use slidetoggle and allow only one div open at a time?

Code: http://codepen.io/anon/pen/sumIx
$('.module article').hide();
});
$('.module-content, .module-photo').click(function() {
for (var i = 0; i < 5; i++) {
$('.module article').slideUp();
} $(this).parent().children('article').slideToggle('slow');
});
If you click on any of the boxes, the previously active div closes as expected.
When you try to close the same div which is active, it opens right back up. How do I keep everything else the same but correct the behavior so that it doesn't reopen?
Try this:
$('.module-content').click(function() {
var $this = $(this).closest('section').find('article');
$('.module article').not($this).slideUp();
$this.slideToggle('slow');
});
http://codepen.io/anon/pen/DBirp
jQuery iterates element collections naturally so your loops are irrelevant in this case. Here's the commented updated code:
$('.module-content').click(function() {
//stores a reference to the clicked section's article
var article = $(this).parent().children('article');
//hides all other articles
$('.module article').not(article).slideUp();
//toggles the clicked one
article.slideToggle('slow');
});
http://codepen.io/anon/pen/dgJDr

jQuery removing elements from DOM put still reporting as present

I have an address finder system whereby a user enters a postcode, if postcode is validated then an address list is returned and displayed, they then select an address line, the list dissappears and then the address line is split further into some form inputs.
The issue i am facing is when they have been through the above process then cleared the postcode form field, hit the find address button and the address list re-appears.
Event though the list and parent tr have been removed from the DOM it is still reporting it is present as length 1?
My code is as follows:
jQuery
// when postcode validated display box
var $addressList = $("div#selectAddress > ul").length;
// if address list present show the address list
if ($addressList != 0) {
$("div#selectAddress").closest("tr").removeClass("hide");
}
// address list hidden by default
// if coming back to modify details then display address inputs
var $customerAddress = $("form#detailsForm input[name*='customerAddress']");
var $addressInputs = $.cookies.get('cpqbAddressInputs');
if ($addressInputs) {
if ($addressInputs == 'visible') {
$($customerAddress).closest("tr").removeClass("hide");
}
} else {
$($customerAddress).closest("tr").addClass("hide");
}
// Need to change form action URL to call post code web service
$("input.findAddress").live('click', function(){
var $postCode = encodeURI($("input#customerPostcode").val());
if ($postCode != "") {
var $formAction = "customerAction.do?searchAddress=searchAddress&custpc=" + $postCode;
$("form#detailsForm").attr("action", $formAction);
} else {
alert($addressList);}
});
// darker highlight when li is clicked
// split address string into corresponding inputs
$("div#selectAddress ul li").live('click', function(){
$(this).removeClass("addressHover");
//$("li.addressClick").removeClass("addressClick");
$(this).addClass("addressClick");
var $splitAddress = $(this).text().split(",");
$($customerAddress).each(function(){
var $inputCount = $(this).index("form#detailsForm input[name*='customerAddress']");
$(this).val($splitAddress[$inputCount]);
});
$($customerAddress).closest("tr").removeClass("hide");
$.cookies.set('cpqbAddressInputs', 'visible');
$(this).closest("tr").fadeOut(250, function() { $(this).remove(); });
});
I think you're running into the same issue I recently ran into. If you have a variable pointing to 5 DIV's (example: var divs = $('.mydivs');) and then you call jQuery's remove() on one of the DIV's, like so: divs.eq(0).remove() you'll see that divs.size() still returns 5 items. This is because remove() operates on the DOM. However... if after calling remove() you then re-set your variable: divs = $('.mydivs'); and get the size you'll now get the correct size of the array. I've added sample code displaying this below:
// get all 5 divs
var d = $('.dv');
// remove the first div
d.eq(0).remove();
// you would expect 4 but no, it's 5
alert(d.size());
// re-set the variable
d = $('.dv');
// now we get 4
alert(d.size());

Categories