jQuery multiple variables in for loops - javascript

This might be simple thing but just can't figure it out.
Let's assume I got fifty similar functions and there's two of them:
var unit = ['red', 'pink']
var unit2 = ['red2', 'red2']
$('#red').click(function() {
if($('#red2').is(':hidden')) {
$('#red2').toggle();
} else {
$('#red2').toggle();}}}
and
$('#pink').click(function() {
if($('#pink2').is(':hidden')) {
$('#pink2').toggle();
} else {
$('#pink').toggle();}}}
and I want to add all these functions in one/two for loops.
I tried this:
for (var i = 0; i < unit.length; i++) {
for (var y = 0; y < unit2.length; y++) {
$('#i').click(function() {
if($('#y').is(':hidden')) {
$('#y').toggle();
} else {
$('#y').toggle();}}}}

.toggle() method detects the visibility of the element itself, there is no need to use if statement, you can use this keyword which refers to the clicked element:
$('#red, #pink').on('click', function() {
// Based on the id property of the clicked element
// this selects #red2 or #pink2 element
$('#' + this.id + '2').toggle();
});
Also note that $('#i') selects an element with ID of i, you should concatenate the strings:
$('#' + i).foo();

You should concatenate strings:
for (var i = 0; i < unit.length; i++) {
$('#' + unit[i])
.attr('data-dst', unit2[i])
.click(function() {
var dst = $(this).attr('data-dst');
$('#' + dst).toogle();
}
}

Related

Right way to dynamically update table

I am getting data over websocket every 10 seconds and i am updating the cells using this function:
agentStatSocket.onmessage = function (e) {
data = JSON.parse(e.data);
//console.log(typeof(data));
for (var i = 0; i < data.length; i++) {
var inboundTd = '#' + data[i]['id'] + '-inbound';
var outboundTd = '#' + data[i]['id'] + '-outbound';
if (data[i]['inboundCalls'] != 0) {
$(inboundTd).html(data[i]['inboundCalls']);
}
if (data[i]['outboundCalls'] != 0) {
$(outboundTd).html(data[i]['outboundCalls']);
}
}
};
This is working pretty fine. However, I see some lag with the table being updated. Currently, there are only 150 rows in the table. I do not know what will be the latency if rows will become 1000 or more.
I have the following questions:
What is the correct approach to design these kinds of tables in which data is changing very frequently? I am not using any library like react or angular. This is plain jQuery.I am using dataTables
jQuery to enhance table view.
One thing to consider is that, in many cases, accessing an element based on an ID is usually a lot quicker in vanilla javascript compared to jquery.
A simple example of that is:
function jsTest() {
for (let i = 0; i < 1000; i++) {
document.getElementById("js").innerHTML = i;
}
}
function jqueryTest() {
for (let i = 0; i < 1000; i++) {
$("#jquery").html(i);
}
}
function startup() {
console.time("javascript");
jsTest();
console.timeEnd("javascript");
console.time("jquery");
jqueryTest();
console.timeEnd("jquery");
}
// For testing purposes only
window.onload = startup;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Javascript: <div id="js"></div>
Jquery: <div id="jquery"></div>
So, you could try changing your code to:
agentStatSocket.onmessage = function (e) {
data = JSON.parse(e.data);
//console.log(typeof(data));
for (var i = 0; i < data.length; i++) {
//var inboundTd = '#' + data[i]['id'] + '-inbound';
//var outboundTd = '#' + data[i]['id'] + '-outbound';
var inboundTd = data[i]['id'] + '-inbound';
var outboundTd = data[i]['id'] + '-outbound';
if (data[i]['inboundCalls'] != 0) {
//$(inboundTd).html(data[i]['inboundCalls']);
document.getElementById(inboundTd).innerHTML = data[i]['inboundCalls'];
}
if (data[i]['outboundCalls'] != 0) {
//$(outboundTd).html(data[i]['outboundCalls']);
document.getElementById(outboundTd).innerHTML = data[i]['outboundCalls'];
}
}
};
You can still use jquery for the rest of your code, if you wish, but simple updates to elements that can be targeted by ID are usually quicker with vanilla javascript.

How to remove element on second click

I need to add elements to container on a first click and delete it on a second one. I guess I'm trying to make it super hard while there is a more elegant and clear solution. Fiddle Link
I was thinking of arrays to create a 1st array for clicked elements and the 2nd one for elements that are already in a container. Then filter the first array through the second one and delete those (unmatched) elements from my container.
var click = +$(this).data('clicks') || 0; // Check if contacts cliked first time
if (click % 2 == 1) { // 2nd click
fruits.splice($.inArray(name, fruits), 1); // Remove Name from an array
$(".test .single").each(function (index, elem) {
compArr.push($(this).text());
});
keyArr = fruits.filter(i => compArr.indexOf(i) !== -1);
var i = 0;
for (; i < keyArr.length; i++) {
$(".name").each(function () {
$(".single:not(:contains('" + keyArr + "'))").remove();
});
} // I guess problem is here
} else { // 1st click
fruits.push(name);
$('.test textarea').css({
'font-size': '12px',
'border': '0'
}).prop('placeholder', '').before('<span class="single">' + name + '></span>');
$('textarea').val('');
}
$(this).data('clicks', click + 1);
For me, this part doesn't work properly. But I would love to hear any of your suggestions even if the entire logic is wrong. Thanks!
var i = 0;
for (; i < keyArr.length; i++) {
$(".name").each(function () {
$(".single:not(:contains('" + keyArr + "'))").remove();
});
}
I've managed to fix it. Added this code:
let deleteSingle = $('.single');
for (let i = 0; i < deleteSingle.length; i++) {
for (let j = 0; j < arrayNewKeys.length; j++) {
if (deleteSingle[i].innerHTML.includes(arrayNewKeys[j])) {
deleteSingle.eq(i).addClass('a');
break;
} else {
deleteSingle.eq(i).removeClass('a');
}
}
}
$('.styleContacts:not(.a)').remove();
if ($('.test > .single.a:only-child')) {
$('.single.a').removeClass('a');
}
Instead of this:
var i = 0;
for (; i < keyArr.length; i++) {
$(".name").each(function () {
$(".single:not(:contains('" + keyArr + "'))").remove();
});
} // I guess problem is here

Dynamic generation of select clear other elements in javascript

I have a for loop that creates as many as I write in an input field. first time I write a number in the imput all is ok... he generates for example 3 fields. When I delete 3 and write 5, he add two objects but he also clear other... if I select an option in the first , I want to keep it selected when I add some other fields....
this is an example: https://jsfiddle.net/exv8s2sq
and this is the code:
Insert number<input type="text" id="number" name="number" ><br><br>
<div id="container"></div>
<script>$('#number').on('keyup', function () {
changenumber(this.value);
});
$('#number').on('paste', function () {
changenumber(this.value);
});
var now = 0;
function changenumber(val) {
container = document.getElementById("container");
var diff = val - now;
if (diff > 0) {
for (var u = now + 1; u <= val; u++) {
container.innerHTML = container.innerHTML +
" Select from options <select id='selectobj" + u + "' name='selectobj" + u + "' style='width:25%;'>" +
"<option>A</option>" +
"<option>B</option>" +
"<option>C</option>" +
"</select><br><br>";
now = u;
}
}
}</script>
thanks
Lorenzo from Rome
Instead of using innerHTML, i would suggest using jQuery as selector and use element.append(selectbox) to add new items. I've updated your fiddle with a working example based on your code:
http://jsfiddle.net/exv8s2sq/1/
There is also room to refactor your code a bit. When using jQuery, native javascript isn't really necessary for dom manipulation.
Wrap your elements in a div with a specific class so you can easily count how many items you already have. Then depending on the number you enter, check whether you need to add or remove elements from your container.
Use jQuery selectors all the way, it is easier to identify your elements, and use the methods it provides such as .each() and .append()
$('#number').on('input', function () {
changenumber(this.value);
});
function changenumber(val) {
if (val !== '') {
// Wrap your elements in a div with a specific class
var select = '<div class="select-wrapper">Select from options <select><option>A</option><option>B</option><option>C</option></select></div>';
// Count number of selects div
var nbOfSelects = $('.select-wrapper').length;
// Check if you need to add or remove elements
if (nbOfSelects < val) {
// Add an element
for (var i = 0; i < (val - nbOfSelects); i++) {
$('#container').append(select);
}
} else {
// Loop through elements
$('.select-wrapper').each(function (i) {
// Remove an element
if (i >= val) {
$(this).remove();
}
});
}
}
}
JSFiddle demo
Try this, it adds an attribute of selected to the previous selected option using an onchange event listener
$('#number').on('keyup', function () {
changenumber(this.value);
});
$('#number').on('paste', function () {
changenumber(this.value);
});
var now = 0;
function changenumber(val) {
container = document.getElementById("container");
var diff = val - now;
if (diff > 0) {
for (var u = now + 1; u <= val; u++) {
container.innerHTML = container.innerHTML +
" Select from options <select onchange='updateDom(this)' id='selectobj" + u + "' name='selectobj" + u + "' style='width:25%;'>" +
"<option>A</option>" +
"<option>B</option>" +
"<option>C</option>" +
"</select><br><br>"; now = u;
}
}
}
function updateDom(s){
s.options[s.selectedIndex].setAttribute("selected","selected")
}

JavaScript Arrays - Iteration Trouble

I'm building a (very) mini JS framework to use for customizing my eBay listings. I do know how to circumvent their "no remote scripts" policy, and I could use jQuery, but this is really an exercise in getting myself better at JS.
I have a function of the global window object which returns a bunch of methods, like this:
window.iq = (function(){
return {
tag: function(tag) {
return document.getElementsByTagName(tag);
},
map: function(el, attr) {
var arr = [];
el = iq.tag(el);
for (i = 0; i < el.length; i++) {
var x = el[i].getAttribute(attr);
arr.push(x);
}
return arr;
},
// A bunch of others like this
};
})();
I'm having trouble (by which I mean I'm utterly stuck) iterating through an array of data-name attributes and hiding or showing images based on whether there is a match. Here's the function:
iq.click('#mblThumbs img', function(){
var dn = iq.map('img', 'data-name');
for (i = 0; i < dn.length; i++) {
if (this.getAttribute('data-name') === dn[i]) {
iq.fadeOut(200, iq.sel('#mblSlide img:not([data-name="' + dn[i] + '"])'));
iq.fadeIn(200, iq.sel('#mblSlide img[data-name="' + dn[i] + '"]'));
}
}
});
I can cycle through the first two images as many times as my heart desires, but as soon as I click on anything past the second image, the function only continues to work for it, and the subsequent indexes in the array. I'm guessing that this is either a problem with my map method, or maybe something to do with array length? I don't know. I'm flummoxed. Any thoughts or suggested are much appreciated.
FIDDLE: http://jsfiddle.net/h8z7c/
The issue is indeed with your click callback. Your fadeout selector is finding the first image that is not data-name=dn[i], which is always either "one" (if you clicked 2), or "two" (if you clicked 1). You either need to use selAll to grab all of the elements that are not the clicked one, or keep track of which one is currently selected. Here are the two ways of fixing it.
// Make sure they are all hidden
iq.click('#mblThumbs img', function(){
var dn = iq.map('img', 'data-name');
for (i = 0; i < dn.length; i++) {
if (this.getAttribute('data-name') === dn[i]) {
var outs = iq.selAll('#mblSlide img:not([data-name="' + dn[i] + '"])');
for (var j = 0; j < outs.length; j++) {
iq.fadeOut(200, outs[j]);
}
iq.fadeIn(200, iq.sel('#mblSlide img[data-name="' + dn[i] + '"]'));
}
}
});
// Or keep track of the currently selected
var selected = "one";
iq.click('#mblThumbs img', function(){
var dn = iq.map('img', 'data-name');
for (i = 0; i < dn.length; i++) {
if (this.getAttribute('data-name') === dn[i] && dn[i] !== selected) {
iq.fadeOut(200, iq.sel('#mblSlide img[data-name="' + selected + '"]'));
iq.fadeIn(200, iq.sel('#mblSlide img[data-name="' + dn[i] + '"]'));
selected = dn[i];
}
}
});

return or send variable to onchange event

I am trying to achieve the same functionality of a function from two separate events. So the function I created is:
function adding_stuff() {
var names = [];
var dates = [];
for(var i = 0; i < this.files.length; i++) {
//adding stuff to names and dates
}
$(".primary .panel-content").append("<ul class='list-unstyled'></ul>");
for(var i in names) {
var li = "<li>";
$(".primary .panel-content ul").append(li.concat(names[i]))
}
}
There are two buttons primary and secondary. I want the same functionality for both the functions but the output in different <div>. Currently the selected <div> is ".primary", however I want this to depend on the button which has been clicked.
The function is triggered using:
$("#primary").onchange = adding_stuff;
$("#secondary").onchange = adding_stuff;
NOTE: primary and secondary are inputs of type file.
You can add additional data when you register the callback, which will be made available within the event handler:
$('#primary').on('change', { target: '.primary' }, adding_stuff);
$('#secondary').on('change', { target: '.secondary' }, adding_stuff);
and then within the handler:
function adding_stuff(ev) {
var cls = ev.data.target; // extract the passed data
...
// file handling code omitted
$(".panel-content", cls).append(...)
}
using jquery's change() event
function adding_stuff(obj,objClass) {
var names = [];
var dates = [];
for(var i = 0; i < obj.files.length; i++) {
//adding stuff to names and dates
}
$("."+ objClass+" .panel-content").append("<ul class='list-unstyled'></ul>");
for(var i in names) {
var li = "<li>";
$("."+ objClass+" .panel-content ul").append(li.concat(names[i]))
}
}
$("#primary").change(function(){
adding_stuff(this,'primary');
});
$("#secondary").change(function(){
adding_stuff(this,'secondary');
});
Try
function adding_stuff(opselector) {
return function() {
var names = [];
var dates = [];
for (var i = 0; i < this.files.length; i++) {
// adding stuff to names and dates
}
var ul = $("<ul class='list-unstyled'></ul>").appendTo(opselector)
for (var i in names) {
ul.append("<li>" + li.concat(names[i]) + "</li>")
}
}
}
$("#primary").change(adding_stuff('.primary .panel-content'));
$("#secondary").change(adding_stuff('.secondary .panel-content'));
You can use $(this).attr("class") inside the function. It will return the class of button who triggered the event.
function adding_stuff() {
var div = $(this).attr("class");
var names = [];
var dates = [];
for(var i = 0; i < this.files.length; i++) {
//adding stuff to names and dates to $div
}
$(div + " .panel-content").append("<ul class='list-unstyled'></ul>");
for(var i in names) {
var li = "<li>";
$(div + " .panel-content ul").append(li.concat(names[i]));
}
}

Categories

Resources