I'm creating a javascript method that populates lists depending on a radio button selected previously. As it depends on animals or plants to populate it, my problem comes when I have to populate it after it's already been populated. I mean, the plants dropdownlist has 88 elements, and the animals is 888, when I try to come back from animals to plants, I get some of the animals. I know that my controller method is working properly because it returns the values I select, so the problem is the javascript method. Here is the code:
if(selector == "sOrder")
alert(document.getElementById(selector).options.length);
for (i = 0; i < document.getElementById(selector).options.length; i++) {
document.getElementById(selector).remove(i);
}
if (selector == "sOrder")
alert(document.getElementById(selector).options.length);
document.getElementById(selector).options[0] = new Option("-select-", "0", true, true);
for (i = 1; i <= data.length; i++) {
document.getElementById(selector).options[i] = new Option(data[i - 1].taxName, data[i - 1].taxRecID);}
Here is the strange thing, when I enter the method I try to erase all the elements of the dropdownlist in order to populate it afterwards. As sOrder is the same selector I had previously selected, I get the elements, the thing is that the first alert I get the proper result, 888, but in the second alert, I should get a 0 right? It shows 444, so when I populate it again it just overrides the first 88 plants and then animals till 444. What am I doing wrong?
Thank you all in advance,
Victor
Essentially you are removing from beginning to the end of the list by using the index, but as you do the index of each consecutive item gets decreased. If you remove from the end to the beginning, the indexes remain the same and you can remove them by index.
Replace
for (i = 0; i < document.getElementById(selector).options.length; i++) {
document.getElementById(selector).remove(i);
}
With
for (i = document.getElementById(selector).options.length -1; i > 0 ; i--) {
document.getElementById(selector).remove(i);
}
A simpler way of removing all items from a select box might be to use:
document.getElementById(selector).innerHTML="";
This would yank the html inside the select. (note I've only tested this in Firefox 3.6.2 but believe this should work in most modern browsers)
I think that in the remove loop there is an error
if(selector == "sOrder")
alert(document.getElementById(selector).options.length);
for (i = 0; i <
document.getElementById(selector).options.length;
i++) {
document.getElementById(selector).remove(i);
}
infact while you're removing elements, document.getElementById(selector).options.length changes.
so during the first iteration you have length=888, then you remove an element
during the second iteration you have length=887 (and i=1) etc.
during the 444th iteration, you have i=443 and length=444 and the loop exit after removing the 444th element.
Related
I've tried to make the following Stackblitz which use ng2-dragula. My problem laid in the moveback() and moveto() function which are supposed to move from one array into another when the element was selected. I was able to detect which element was selected but unable to move them completely (some element was left over).
Related Code:
moveback() {
let target = document.getElementsByClassName('target');
for (let i = 0; i < target.length; i++) {
if (target[i].className.includes('ex-over')) {
this.removeClass(target[i], 'ex-over');
this.data.push({ name: target[i].innerHTML });
this.target.splice(i, 1);
}
}
}
moveto() {
let target = document.getElementsByClassName('data');
for (let i = 0; i < target.length; i++) {
if (target[i].className.includes('ex-over')) {
this.removeClass(target[i], 'ex-over');
this.target.push({ name: target[i].innerHTML });
this.data.splice(i, 1);
}
}
}
I've found two related(1), related(2) question which asks similar thing but it's not working in my case. My approach was detecting either the element contains a certain class name then remove the class and move them to another array and remove it from the original array. But it's not working like intended.
Update your for condition as below.
for (let i = target.length - 1; i >= 0; i--) {...}
The issue with your logic is, If you select 2nd & 3rd element and apply moveto then for 2nd element it will work fine. But then in your actual this.target array will be changed. Now 3rd element will become 2nd because of your line this.target.splice(i, 1);. So when you move 3rd element in for loop iteration, it will actually move 4th one.
Check with Updated fiddle Here
EDIT: I don't want to skip index 1. I want to skip the current (clicked on) element. Also, see below for more of the code as requested. You'll see that I have a class CatListItem and five instances of that class in an array allCatListItems.
Here's some context for the question: I have a list of cats. When I click on a cat's name (list item), I want that cat's picture and other info to be appended to the page (got that down). When a cat is clicked, I also want any other cat that is being displayed to be hidden (that way there is only one cat on the screen at a time).
I'm trying to accomplish this with a for loop, but obviously if it iterates over every item in the array, then when I click an item, the cat being clicked will be hidden as well.
I want to skip the current item in the array and only run the code on the other items. Using continue, I know I can skip a specific item (item 1 in the below example). This will run my code on every item in the array except that at index one. But how can I make that continue dynamic? Meaning... how can I hide all of the cats, except the one being currently clicked?
Here's the loop that skips index 1:
CatListItem.prototype.hideCats = function() {
allCatListItems.forEach(function(cat) {
cat.a.addEventListener('click', function() {
for (var i = 0; i < allCatListItems.length; i++) {
if (i === 1) {
continue;
}
allCatListItems[i].img.className = 'hide';
};
});
});
}
var allCatListItems = [
catListItem1 = new CatListItem('El', 'images/el.jpg', 'el'),
catListItem2 = new CatListItem('Widdle Baby', 'images/widdle-baby.jpg', 'widdle-baby'),
catListItem3 = new CatListItem('Mama', 'images/mama.jpg', 'mama'),
catListItem4 = new CatListItem('Legion', 'images/legion.jpg', 'legion'),
catListItem5 = new CatListItem('Boy', 'images/boy.jpg', 'boy'),
];
EDIT: Here's a fiddle.JSFIDDLE Click the names to see the functionality without the hideCats function. Then uncomment where it says to to see my issue.
I'm starting to think maybe a for loop isn't the best option?
In that case compare the event.target(its the element clicked)
EDIT: allCatListItems[i] needs it's .a property attached to it in the if statement (this is what contains the anchor element). This is because the event listener is grabbing an anchor tag, so e.target will be returning an anchor tag as well. The if statement will never return as true if you aren't comparing the same type of element.
cat.a.addEventListener('click', function(e) {
for (var i = 0; i < allCatListItems.length; i++) {
if (allCatListItems[i].a === e.target) {
continue;
}
allCatListItems[i].img.className += ' hide';
}
});
Here is a jsfiddle, it doesn't use the same element names, but it should be doing what you want. https://jsfiddle.net/5qb4rwzc/
$('li').on('click', function() {
var index = $(this).index();
var items = document.getElementsByTagName('li');
for(var i = 0; i < items.length; i++) {
if(i === index) continue;
items[i].style = "display:none;";
}
});
Its really depend on how you call the function "hideCat". Realizing that each time that function is called, more eventListeners are add to every cat item. Each time a cat is clicked, more than one event fired. Perhaps you should re-structure how to attach eventListeners to each cat item.
I would be surprised if this hasn't been asked in the past. I was unable to find something that works. I have an array of strings and I would like to create a new array with only the last 4 characters of each of the elements. The code is below. What do I need to change to make it work?
for(i=0; i<userScanDataObjects.length;i++){
// if statement used to select every alternative element from the array as th rows are duplicated
if (i%2 !==0){
deviceID_raw.push(userScanDataObjects[i].deviceID.value);
deviceID.push(deviceID_raw[i].substring(5,8));
}
['11114444', '22224444'].map(function(string) { return string.substring(string.length - 4, string.length) })
You can use Array.map depending on your targeted browser support/polyfills. And you can do the following:
for( var i = 0; i < array.length; ++ i ) {
array[i] = array[i].substr(-4);
}
I am trying to use JavaScript to remove all the elements with a certian name, but it is only removing the first one.
My code is:
var ele= document.getElementsByName("javascriptaudio");
for(var i=0;i<ele.length;i++)
{
ele[i].parentNode.removeChild(ele[i]);
}
Can anyone tell me what is wrong?
Thanks
I don't have enough rep to comment on Álvaro G. Vicario. The reason that it works is that the element is removed from ele when it is removed from the DOM. Weird.
The following code should work equally well:
var ele= document.getElementsByName("javascriptaudio");
len = ele.length;
parentNode = ele[0].parentNode;
for(var i=0; i<len; i++)
{
parentNode.removeChild(ele[0]);
}
Try removing them backwards:
var ele = document.getElementsByName("javascriptaudio");
for(var i=ele.length-1;i>=0;i--)
{
ele[i].parentNode.removeChild(ele[i]);
}
The problem is that removing elements from ele shifts indexes: if you have 5 items (0 to 4) and remove item 0 you then have 4 items ranging from 0 to 3 (4 becomes 3, 3 becomes 2, etc.); you should then remove item 0 but your i variable has already incremented to 1.
you need to save length of array outside the loop because it is evaluated with each pass if you place it inside loop criteria. That way you will have correct amount of iterations. Furthermore you need to delete first item in loop each time because with each pass you lose 1 item so you will be outside of range at the end of process.
var ele = document.getElementsByName("javascriptaudio");
var elementsCount = ele.length;
for(var i=0;i<ele.length;i++){
ele[0].parentNode.removeChild(ele[0]);
}
Try the following jQuery code:
$("[name=javascriptaudio]").remove();
I'm trying to re-sort the child elements of the tag input by comparing
their category attribute to the category order in the Javascript
variable category_sort_order. Then I need to remove divs whose category attribute
does not appear in category_sort_order.
The expected result should be:
any
product1
product2
download
The code:
<div id="input">
<div category="download">download</div>
<div category="video">video1</div>
<div category="video">video2</div>
<div category="product">product1</div>
<div category="any">any</div>
<div category="product">product2</div>
</div>
<script type="text/javascript">
var category_sort_order = ['any', 'product', 'download'];
</script>
I really don't even know where to begin with this task but if you could please provide any assistance whatsoever I would be extremely grateful.
I wrote a jQuery plugin to do this kind of thing that can be easily adapted for your use case.
The original plugin is here
Here's a revamp for you question
(function($) {
$.fn.reOrder = function(array) {
return this.each(function() {
if (array) {
for(var i=0; i < array.length; i++)
array[i] = $('div[category="' + array[i] + '"]');
$(this).empty();
for(var i=0; i < array.length; i++)
$(this).append(array[i]);
}
});
}
})(jQuery);
and use like so
var category_sort_order = ['any', 'product', 'download'];
$('#input').reOrder(category_sort_order);
This happens to get the right order for the products this time as product1 appears before product2 in the original list, but it could be changed easily to sort categories first before putting into the array and appending to the DOM. Also, if using this for a number of elements, it could be improved by appending all elements in the array in one go instead of iterating over the array and appending one at a time. This would probably be a good case for DocumentFragments.
Just note,
Since there is jQuery 1.3.2 sorting is simple without any plugin like:
$('#input div').sort(CustomSort).appendTo('#input');
function CustomSort( a ,b ){
//your custom sort function returning -1 or 1
//where a , b are $('#input div') elements
}
This will sort all div that are childs of element with id="input" .
Here is how to do it. I used this SO question as a reference.
I tested this code and it works properly for your example:
$(document).ready(function() {
var categories = new Array();
var content = new Array();
//Get Divs
$('#input > [category]').each(function(i) {
//Add to local array
categories[i] = $(this).attr('category');
content[i] = $(this).html();
});
$('#input').empty();
//Sort Divs
var category_sort_order = ['any', 'product', 'download'];
for(i = 0; i < category_sort_order.length; i++) {
//Grab all divs in this category and add them back to the form
for(j = 0; j < categories.length; j++) {
if(categories[j] == category_sort_order[i]) {
$('#input').append('<div category="' +
category_sort_order[i] + '">'
+ content[j] + '</div>');
}
};
}
});
How it works
First of all, this code requires the JQuery library. If you're not currently using it, I highly recommend it.
The code starts by getting all the child divs of the input div that contain a category attribute. Then it saves their html content and their category to two separate arrays (but in the same location.
Next it clears out all the divs under the input div.
Finally, it goes through your categories in the order you specify in the array and appends the matching child divs in the correct order.
The For loop section
#eyelidlessness does a good job of explaining for loops, but I'll also take a whack at it. in the context of this code.
The first line:
for(i = 0; i < category_sort_order.length; i++) {
Means that the code which follows (everything within the curly brackets { code }) will be repeated a number of times. Though the format looks archaic (and sorta is) it says:
Create a number variable called i and set it equal to zero
If that variable is less than the number of items in the category_sort_order array, then do whats in the brackets
When the brackets finish, add one to the variable i (i++ means add one)
Then it repeats step two and three until i is finally bigger than the number of categories in that array.
A.K.A whatever is in the brackets will be run once for every category.
Moving on... for each category, another loop is called. This one:
for(j = 0; j < categories.length; j++) {
loops through all of the categories of the divs that we just deleted from the screen.
Within this loop, the if statement checks if any of the divs from the screen match the current category. If so, they are appending, if not the loop continues searching till it goes through every div.
Appending (or prepending) the DOM nodes again will actually sort them in the order you want.
Using jQuery, you just have to select them in the order you want and append (or prepend) them to their container again.
$(['any', 'product', 'video'])
.map(function(index, category)
{
return $('[category='+category+']');
})
.prependTo('#input');
Sorry, missed that you wanted to remove nodes not in your category list. Here is the corrected version:
// Create a jQuery from our array of category names,
// it won't be usable in the DOM but still some
// jQuery methods can be used
var divs = $(['any', 'product', 'video'])
// Replace each category name in our array by the
// actual DOM nodes selected using the attribute selector
// syntax of jQuery.
.map(function(index, category)
{
// Here we need to do .get() to return an array of DOM nodes
return $('[category='+category+']').get();
});
// Remove everything in #input and replace them by our DOM nodes.
$('#input').empty().append(divs);
// The trick here is that DOM nodes are selected
// in the order we want them in the end.
// So when we append them again to the document,
// they will be appended in the order we want.
I thought this was a really interesting problem, here is an easy, but not incredibly performant sorting solution that I came up with.
You can view the test page on jsbin here: http://jsbin.com/ocuta
function compare(x, y, context){
if($.inArray(x, context) > $.inArray(y, context)) return 1;
}
function dom_sort(selector, order_list) {
$items = $(selector);
var dirty = false;
for(var i = 0; i < ($items.length - 1); i++) {
if (compare($items.eq(i).attr('category'), $items.eq(i+1).attr('category'), order_list)) {
dirty = true;
$items.eq(i).before($items.eq(i+1).remove());
}
}
if (dirty) setTimeout(function(){ dom_sort(selector, order_list); }, 0);
};
dom_sort('#input div[category]', category_sort_order);
Note that the setTimeout might not be necessary, but it just feels safer. Your call.
You could probably clean up some performance by storing a reference to the parent and just getting children each time, instead of re-running the selector. I was going for simplicity though. You have to call the selector each time, because the order changes in a sort, and I'm not storing a reference to the parent anywhere.
It's seems fairly direct to use the sort method for this one:
var category_sort_order = ['any', 'product', 'download'];
// select your categories
$('#input > div')
// filter the selection down to wanted items
.filter(function(){
// get the categories index in the sort order list ("weight")
var w = $.inArray( $(this).attr('category'), category_sort_order );
// in the sort order list?
if ( w > -1 ) {
// this item should be sorted, we'll store it's sorting index, and keep it
$( this ).data( 'sortindex', w );
return true;
}
else {
// remove the item from the DOM and the selection
$( this ).remove();
return false;
}
})
// sort the remainder of the items
.sort(function(a, b){
// use the previously defined values to compare who goes first
return $( a ).data( 'sortindex' ) -
$( b ).data( 'sortindex' );
})
// reappend the selection into it's parent node to "apply" it
.appendTo( '#input' );
If you happen to be using an old version of jQuery (1.2) that doesn't have the sort method, you can add it with this:
jQuery.fn.sort = Array.prototype.sort;