Sorting elements in array - javascript

I have an array containing a list of images that I have stored online:
var imgs = ["img1","img2","img3"...];
They are displayed as such:
var child, i;
for (i = 0; i < imgs.length; i++) {
child.style.backgroundImage = 'url(https://mywebsite/images/' + imgs[i] + '.jpg)';
$('body').append(child);
}
However, I'm looking to try to display the images based on their attribute, that can change depending on a user action.
This attribute will look like this:
child.setAttribute("class", 0);
Say for example, the user decides to click on an image, then the attribute of the image he clicked on will increment. So the class will no longer be '0' but '1'. And because of so, the image will be placed before the others in the array.
Assuming 'img2' was clicked, then the array would look like:
imgs = ["img2","img1","img3"...];
I feel like the method I'm going by is inefficient and I have considered using objects or even 2d arrays, but I'm not sure where to start and lack experience. However, if this isn't "such a bad way" to get started, then I'd appreciate if someone showed me how I could move the elements in the array.
Thanks in advance.

You can try this:
$('body').find("img").on("click", function()
{
$(this).data("count", (Number($(this).data("count")) + 1));
reOrderImages();
});
function reOrderImages()
{
var imgs = $('body').find("img");
for (var i = 0; i < imgs.length; i++)
{
var img1 = $(imgs[i]);
var img2 = (imgs.length > (i + 1) ? $(imgs[(i + 1)]) : false);
if (img2 && Number(img1.data("count")) < Number(img2.data("count")))
{
img1.before(img2);
}
}
};
Fiddle. Here I'm using data attributes instead of the class attribute, which isn't designed for your case. It just swaps the elements with before() method.

You can use array.sort with a compare function. getClass method here is some method which returns current class attribute of an image by it's url
imgs.sort(function(img1, img2){
var class1 = getClass(img1);
var class2 = getClass(img2);
return class1 - class2;
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

Related

Object array gets overwritten by the last object

I have this array of objects being loaded:
$(document).ready(function () {
$("<div></div>").load("/Stats/Contents #stats", function () {
statcount = $(".list-group-item", this).length;
for (var j = 0; j < statcount; j++) {
statList.push(stat);
}
for (var i = 0; i < statcount; i++) {
statList[i].statId = document.getElementById("statId-" + (i + 1) + "").value;
statList[i].productDescription = document.getElementById("productType-" + (i + 1) + "").value;
statList[i].lastMeasuredInventoryAmount = document.getElementById("statLastMeasureAmount-" + (i + 1) + "").value;
}
)}
)}
.... and so on
Then I get the changed values and save them, however, in the ajax post call, all of the array objects are same (the last one assigned), looks like they get overwritten.
Any ideas? I saw these deferred/promise type code but not sure if there's simpler way.
Thanks.
It sounds like you take the statList array and then push that up to the server, with any respective change. Rather than building and maintaining a list like this, have you thought of switching it around and just grabbing the results out of the markup and building up the array at save point?
$("btnSave").on("click", function(e) {
var data = [];
$(".list-group-item").each(function() {
data.push({
statId: $(this).find(".statid").val(),
.
.
})
});
You wouldn't need to give every element an ID (but could) as my sample uses CSS classes to find the element within the current list item. Additionally, if these are inputs, you could serialize them from the root element more efficiently...

For loop within a for loop? - Javascript

newbie javascript question. I made sure to research as much as I could before posting here, I've tried many solutions but could be searching for the wrong thing.
I've attached an image below of the issue I have. I'm trying to retrieve everything in the dark blue boxes, but I can't identify those input tags as there is nothing unique about them, I can however identify their parent divs by the class 'f-active'. When the divs have that class they have been selected by the user which is what I am interested in.
My attempt so far
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var i=0; i < childArray.length; i++){
if(childArray[i].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[i].name);
// console.log(catNameCollection);
}
}
}
I tried to use a for loop to get all the parents, then use another for loop to select the children (input tags) and then grab the name attribute, however it is just outputing numbers. I did originally try to create 'divArray' as document.querySelectorAll('div.add-filter.f-active').children, but this and then grab the name attribute in the for loop, but this didn't return anything at all.
Any help anyone could offer would be greatly appreciated, I'd love to know what I'm doing wrong.
Thank you!
Your i is same for both loops. Use:
var divArray = document.querySelectorAll('div.add-filter.f-active');
var arr = [];
for(var i=0; i < divArray.length; i++){
var childArray = divArray[i].children;
// console.log(childArray);
for(var k=0; k < childArray.length; k++){
if(childArray[k].tagName == "INPUT"){
var catNameCollection = arr.push(childArray[k].name);
// console.log(catNameCollection);
}
}
}
Classic for-loops usually aren't the best tool for iterating through DOM elements - they add a lot of clutter and are error-prone, especially when you have to nest them.
In your case it'd be simpler to instead modify your query to directly grab all input elements with a div.f-active parent, then extract the names by iterating through them with a forEach. For example (using ES6 or higher):
const arr = [];
// Get list of all <input> elements that have <div> element parents with class f-active.
const nodes = document.querySelectorAll('div.add-filter.f-active > input');
// Extract name from each input element matched by your selector.
nodes.forEach(node => arr.push(node.name));
Or if you're stuck using ES5:
var arr = [];
var nodes = document.querySelectorAll('div.add-filter.f-active > input');
nodes.forEach(function(node) {
arr.push(node.name);
});
Here's a quick JSFiddle I put together to demonstrate the concept for you. (You'll need to open the console to see the result)
Hopefully that helps :)

how can i create an image array, each with a unique id using getElementById in javascript?

Here is the code I have but when I try to use the image reference its value is null. I want to be able to use it to access another image in a table and change the src property to one in the preloadImages array. I have already made the table but I can't get a reference to the individual elements of the preloadImages array. I want each image to have a unique ID using document.getElementById('id') Please help!! Much appreciated!
var preloadImages = new Array();
SIZE = 52;
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image();
preloadImages[h].src = h+'.png';
preloadImages[h].height =100;
preloadImages[h].width = 70;
preloadImages[h].id = "cardImage"+h+"";
var cardImageRef = document.getElementById("cardImage"+h+"");
document.write(cardImageRef+'h'); //This line is for testing, it just returns null
}
You cannot use getElementById() unless the element exists in the DOM tree. This modification will add them to DOM:
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image(70, 100); // size here
preloadImages[h].src = h+ ".png";
preloadImages[h].id = "cardImage" + h;
document.body.appendChild(preloadImages[h]); // add to DOM, or some parent element
var cardImageRef = document.getElementById("cardImage" + h);
document.write(cardImageRef + "h"); //This line is for testing, it just returns null
}
However, since you already have the elements referenced in an array you could simply look the element up there using the index for the id (instead of using an id at all). This tend to be faster if your page layout has several elements.
var image = preloadImages[index];
If you where using images not identifiable by index you could do (but probably not relevant in this case):
function getImage(id) {
for(var i = 0; i < preloadImages.length; i++) {
if (preloadImages[i].id === id) return preloadImages[i];
}
return null;
}
So you could use it here, for example:
for(var h=0; h<SIZE; h++){
preloadImages[h] = new Image(70, 100); // size here
preloadImages[h].src = h + ".png";
preloadImages[h].id = "cardImage" + h;
}
document.write(getImage("cardImage0"));
The function document.getElementById() can only be used to reference elements that have already been added to the DOM.
However, if you wish to reference images by their id you can build an object instead and use the id as a key:
var preloadImages = {};
SIZE = 52;
for(var h = 0; h < SIZE; ++h) {
var i = new Image();
i.id = 'cardImage' + h;
i.src = h + '.png';
i.width = 70;
i.height = 100;
preloadImages[i.id] = i;
}
Afterwards you can refer to each image object by its id like so:
var img = preloadImages['cardImage2'];
// do something with image
document.body.appendChild(img); // add to page
There's no reason to use IDs here. In general, it's a "code smell" to construct IDs dynamically and then use getElementById to find them in your document. You can instead just work with the elements themselves.
After the code you posted runs, then preloadImages array contains the added elements. You don't need to use getElementById to find the element you just constructed! And as you found out, you can't use it at all until the element is inserted into the DOM, which you're not ready to do yet!
So in your table logic, you can simply insert the images you created directly using the references you already have in preloadImages:
function makeTable(images) {
var table = document.createElement('table');
var tr = document.createElement('tr');
table.appendChild(tr);
for (var i = 0; i < images.length; i++) {
var td = document.createElement('td');
tr.appendChild(td);
// put the images in the TD
td.appendChild(images[i]);
}
return table; // to be inserted somewhere
}
document.body.appendChild(makeTable(preloadImages));
I think you're getting confused because some people have gotten into the habit of thinking of IDs as the only way of referring to elements. They construct IDs by concatenating strings, then retrieve the element when they need it by using getElementById. In essence, they are using the document itself as a kind of global bag of things from which you retrieve things using ID as a kind of variable name. It's better in such cases to just work with the elements themselves, held in JavaScript variables or arrays.

How to get the next element of an array with Jquery onclick

Hi all i am trying to change the html of an object from an array of htmls. But i am having problem iterating properly. I managed to make it work once
EDIT
After a few complains about the clarity of my question I will rephrase it. I have a div panel called .trpanel and a button called #trigger2 (it is a next button). Then I have a series of divs with texts that contain translations. I want when I press the button (called next) to cycle through the translations one by one on the trpanel.
var ltranslation = [];
ltranslation[0] = $("#translation-en-1").html();
ltranslation[1] = $("#translation-ur-en").html();
ltranslation[2] = $("#translation-fr-en").html();
ltranslation[3] = $("#translation-it-en").html();
ltranslation[4] = $("#translation-sp-en").html();
ltranslation[5] = $("#translation-po-en").html();
ltranslation[6] = $("#translation-fr-en").html();
ltranslation[7] = $("#translation-de-en").html();
var l= ltranslation;
$("#trigger2").off('click').on('click',function(){
for (var i = 0; i <= ltranslation.length; i++){
if (i==7){i=0;}
$(".trpanel").html.ltranslation[i]; or ???//replace().ltranslation[]+i??? the code throws errors
}
});
I am quite new to Javascript and i am getting a bit confused with the types of objects and arrays and loops. I managed once to add the htmls but without replacing them ... so they all came one after the other. The i tried to change the code and it hasn't worked since. Any help will be greatly appreciated.
A lot of guessing, but seems like you are trying to do this :
var trans = $('[id^="translation-"]'),
idx = 0;
$("#trigger2").on('click',function(){
$(".trpanel").html( trans.eq(idx).html() );
idx = idx > 6 ? 0 : idx+1;
});
FIDDLE
I think you are trying to do this:
if (i == 7) {
i = 0; // I don't really know why you are doing this, but it will reset the loop
}
$(".trpanel").html(ltranslation[i]); //I'm passing ltranslation[i] to the html method. Instead of .html.ltranslation[i].
}
Also, without seeing any html, I'm not sure but I think you may want to iterate over .trpanel ?
Something like:
$(".trpanel").eq(i).html(ltranslation[i]);
Another thing (so you can make your code clearer I think). You can abstract the array population in a function, like this:
var ltranslation = [];
var languages = ["en-1", "ur-en", "fr-en", "it-en", "sp-en", "po-en", "fr-en", "de-en"];
$.each(languages, function(index) {
ltranslation[index] = $("#translation-" + this).html();
});
// Then you can use ltranslation
If you want to flip through several translations I would implement it that way:
var translations=["hej","hello", "hallo","hoy"];
var showTranslation=function(){
var current=0;
var len=translations.length;
return function(){
var direction=1;
if (current>=len) current=0;
$("#text").text(translations[current]);
current+=direction;
}
}();
$("#butt").on("click", showTranslation);
Fiddle: http://jsfiddle.net/Xr9fz/
Further: You should give your translations a class, so you could easily grab all of them with a single line:
$(".translation).each(function(index,value){ ltranslation.push(value); })
From the question : I managed once to add the htmls but without replacing them -
I think you want to add all of these items into $(".trpanel"). First, dont take the HTML of each element, clone the element itself :
//method ripped from Nico's answer.
var ltranslation = [];
var languages = ["en-1", "ur-en", "fr-en", "it-en", "sp-en", "po-en", "fr-en", "de-en"];
$.each(languages, function(index) {
ltranslation[index] = $("#translation-" + this).clone();
});
Then you could append everything into the container, so add the htmls but without replacing them. append takes in an array without replacing the previous html.
$("#trigger2").off('click').on('click',function() {
$(".trpanel").append(ltranslation);
});
I don't know what exactly you're tring to do, but I've put comments in your code to help you better understand what your code is doing. The net effect of your code is this (which I doubt you want) :
$("#trigger2").off('click').on('click',function(){
$(".trpanel").html(ltranslation[7]);
});
This is your code with some comments and minor changes
var ltranslation = [];
ltranslation[0] = $("#translation-en-1").html();
ltranslation[1] = $("#translation-ur-en").html();
ltranslation[2] = $("#translation-fr-en").html();
ltranslation[3] = $("#translation-it-en").html();
ltranslation[4] = $("#translation-sp-en").html();
ltranslation[5] = $("#translation-po-en").html();
ltranslation[6] = $("#translation-fr-en").html();
ltranslation[7] = $("#translation-de-en").html();
var l= ltranslation;
$("#trigger2").off('click').on('click',function(){
for (var i = 0; i < ltranslation.length; i++){
//if (i==7){i=0;} <-- This will cause an infinite loop won't it? are you trying to reset i? i will reset next time loop is called,
$(".trpanel").html(ltranslation[i]); //<-- this will overwrite elements with class .trpanel ltranslation.length times...
///you'll see only the value of translation[7] in the end
}
});
EDIT
To do what you want to do based on your comments, try this:
var ltranslation = [];
ltranslation[0] = $("#translation-en-1").html();
ltranslation[1] = $("#translation-ur-en").html();
ltranslation[2] = $("#translation-fr-en").html();
ltranslation[3] = $("#translation-it-en").html();
ltranslation[4] = $("#translation-sp-en").html();
ltranslation[5] = $("#translation-po-en").html();
ltranslation[6] = $("#translation-fr-en").html();
ltranslation[7] = $("#translation-de-en").html();
var counter = 0;//a global counter variable
$("#trigger2").click(function(){ //eeverytime button is clicked do this
$(".trpanel").html(ltranslation[counter]); //set the html to an element of array
counter++; //increment counter
if(counter==ltranslation.length) //reset the counter if its bigger than array len
counter=0;
});

Combining jQuery with native JS in DOM creation

I'm reusing an old application of mine and want to change the code so I'm applying the DOM structure that I build up to a node's class instead of it's id.
Below is a piece of the code and as you can see I try to combine jQuery (getting the node by it's class) with the old structure, but something doesn't work properly here.
Is it possible to combine jQuery and JS native like this?
If not, is there another way to accomplish what I want to do?
var gamearea = $('<div/>', {
text': 'testarea',
class': 'gamearea'
}).appendTo('.memory:last');
alert("this.rows: " + this.rows);
for (var j = 0; j < this.rows; j++){
var box = document.createElement('div');
for (var i = 0; i < this.cols; i++){
var iterator = (this.cols * j) + i;
var img = document.createElement('img');
var aNod = document.createElement('a');
aNod.href = "#";
img.src = "pics/0.png";
aNod.appendChild(img);
box.appendChild(aNod);
}
gamearea.appendChild(box);
}
You should be able to get it working by changing gamearea.appendChild(box); to gamearea[0].appendChild(box);
The reason behind that is you can get the bare DOM element for a jQuery extended object by simply doing obj[0], where obj is a jQuery extended object obtained like obj = $(...) etc. And the appendChild method in your code is a method of bare DOM element.

Categories