check dynamic array against static array - javascript

I need some javascript/jquery for this. I have two arrays, one static array that is hard coded and another dynamic array that is user driven. For example the values in each array represent a div. Each div is represented in the static array. I want to be able to show only the divs that are present in the dynamic array. So if the dynamic array changes, it shows the divs present in the dynamic array and hides the divs not present. I also want to be able to run a function connected to each div, ie box1 has a function that needs called if it is displayed.
var static_list = new Array("box1","box2","box3");
var dynamic_list = new Array("box1","box3");

I'm assuming that dynamic_list is a subset of static_list.
$.each(static_list, function(index, value) {
$('#' + value).hide();
});
$.each(dynamic_list, function(index, value) {
$('#' + value).show();
});
$.each() iterates through a collection calling the callback function for each element. The first $.each() hides all elements with IDs found in the static_list. The second $.each() shows all elements with IDs found in the dynamic_list. Running both in that order hides all div elements in static_list except for those found in the dynamic_list.

You could use a simple comparator function:
// Return true if value is in array
function isIn(value, array) {
var i = array.length;
while (i--) {
if (array[i] == value {
return true;
}
}
return false;
}
then you can see if a value is in an array using:
if (isIn(value, array)) {
// do something with/to value
}

You can achieve your result by using $.each(), $.inArray(), and .toggle()
$.each(static_list, function(index, value){
var showHide = $.inArray(value, dynamic_list) >= 0;
$("#" + this).toggle(showHide);
});
Code example on jsfiddle.

Related

Array not updating when using JQuery

I'm trying to splice an array in order to delete an object from my array. I'm using angular-formly to display the forms and AngularJs and JQuery in order to handle the data.
The JQuery
$(document).on("click", ".delete-me", function () {
var id = $(this).parent().find('input, select, textarea').attr('id');
var splitid = id.split("_")[3];
angular.element(document.getElementById('View2Ctrl')).scope().removeField(splitid);
$(this).closest('.formly-field').remove();
});
The reason for the split and is that formly wraps an id like formly_1_input_textField-1_0.
The Angular Function
$scope.removeField = function (id) {
for(var i=0;i<$scope.formFields.length;i++){
if($scope.formFields[i].key == id){
$scope.formFields.splice(id, 1);
console.log($scope.formFields.length);
$scope.currentId = i;
}
}
};
The console log is displaying the actual length of the array however I have {{formFields.length}} on the template and it does not update, as well as {{formFields}} still showing the data in the array. I suspect that JQuery updating the DOM isn't being watched by Angular and have tried calling $scope.$apply() manually to no avail.
You have a couple of logic errors in your function:
When you remove an item from the array, you increase i anyway, which means you'll miss out the next item if it's a match.
You're using id instead of i in the splice
That second one is probably the culprit, but once you'd fixed that, the first would have bitten you (unless the id values are unique, as the name implies; in which case, you should probably terminate the loop when you find it).
Updates:
$scope.removeField = function (id) {
var i=0;
while (i<$scope.formFields.length){ // Use a while loop
if($scope.formFields[i].key == id){
$scope.formFields.splice(i, 1); // Use i, not id
console.log($scope.formFields.length);
$scope.currentId = i;
// DON'T increment i here, you've removed the element
// Or if IDs are unique, just terminate the loop
} else {
++i;
}
}
};

Using Jquery Filter as a way to filter links on a page

I have a list of links on a page and a set of checkboxes that should filter the links so that only the ones that have the specific criteria will be active. My rudimentary attempt to do this involves creating an array of all the filter options that are active and running a .filter(Array) to make those active and a .not(Array) to disable the other links.
The problem is that if more than one filter options is selected any link that matches either filter option will be active. When in reality what i want is only the links that match ALL the filter options to be active.
Here is my stripped down version in jsFiddle
var filterAll = ["filter-F_0", "filter-F_1", "filter-F_2", "filter-F_3", "filter-F_4", "filter-P_0", "filter-P_1", "filter-P_2", "filter-P_3", ]
var filterActive = [];
function filterApps(){
if(filterActive.length == 0)
{
filterReset();
return;
}
var arrActive = $.map(filterActive, function (val) {
return '[' + val + ']'
}).join(",")
addToLog("arr = " + arrActive);
$(".appLink").filter(arrActive).css(activeAppCSS).addClass("active").removeClass("disable");
$(".appLink").not(arrActive).css(disabledAPPCSS).addClass("disable").removeClass("active");}
You have complicated many things here, using attribute's name for filtering the elements is a terrible idea (sorry), you could use data-* attributes that store the filtering criteria in an array. If I have understood the question correctly something like the following should work, this solution reads the attributes' name using attributes property, it should be noted that it's not the most efficient way of doing the task and as Array object's .filter() method is used it doesn't work in older browsers that don't support ES5, for supporting those browsers you can use a shim.
var $links = $('.appLink');
var $checkboxes = $('input[type=checkbox]').on('change', function () {
// Creating an array of values
var checked = $checkboxes.filter(':checked').map(function () {
return this.value.toLowerCase();
}).get();
// Filtering the .appLink elements by reading the attributes
// and comparing the filtered array's length with the checked one
$links.removeClass('matched').filter(function () {
return [].slice.call(this.attributes).filter(function (a) {
return a.name.indexOf('filter') === 0;
}).filter(function(f) {
return $.inArray(f.name.replace('filter-', ''), checked) > -1;
}).length === checked.length;
}).addClass('matched');
});
http://jsfiddle.net/85tTp/
In case that you want to use data-* properties, you can define an attribute like data-filter='["f1", "f2", ""]' for the elements and use jQuery .data() method for reading them:
$links.removeClass('matched').filter(function () {
return $(this).data('filter').filter(function(f) {
return $.inArray(f, checked) > -1;
}).length === checked.length;
}).addClass('matched');

How to check if two jQuery selectors have selected the same element

Is there a way for me to progamatically determine if two jQuery selectors have selected the same exact element? I am trying to loop over a set of divs and skip one of them. What I would like is something like this:
var $rows, $row, $row_to_skip;
$rows = $('.row-class')
$row_to_skip = $('#skipped_row')
$.each($rows, function (id, row) {
$row = $(row);
if (!$row == $row_to_skip) {
// Do some stuff here.
};
});
You can pass jQuery objects into .not():
$rows.not($row_to_skip).each(function() {
...
});
You can use .is()
if (!$row.is($row_to_skip)) {
// Do some stuff here.
};
You can compare the actual DOM element selected by jQuery:
var row_to_skip = $row_to_skip.get(0);
$.each($rows, function (id, row) {
if (row !== row_to_skip) {
// Do some stuff here.
}
});
Two jQuery objects will always be different from each other, even if they select the same elements (just like two empty objects are different).
However in your case, instead of comparing inside the loop, it's cleaner to just remove the element from the set:
$('.row-class').not("#skipped_row").each(function() {
// do stuff
});

select an array of elements and use them

Using this syntax:
var position = array($('#ipadmenu > section').attr('data-order'));
I cannot get my code to work. I have never used arrays before so im kind of lost on how to use them. (especially in jquery).
How would I make an array of all section elements and associate the value of data-order to that list. Example:
first section - data-order:1
second section - data-order:2
etc and then use that info afterwards.
Thank you!
Since .attr just gets one attribute -- the first one found by the jQuery selector -- you need to build your array element by element. One way to do that is .each (you can also use .data to extract data attributes):
var position = new Array;
$('#ipadmenu > section').each(function() {
position.push($(this).data('order'));
});
alert(position[0]); // alerts "1"
This will be an indexed array, not an associative array. To build one of those (which in JavaScript is technically an object, not any kind of array) just change the inner part of your .each loop:
var position = {};
$('#ipadmenu > section').each(function(i) {
position["section"+i] = $(this).data('order');
});
The resulting object position can now be accessed like:
alert(position['section1']); // alerts "1"
A different approach involves using jQuery.map, but since that only works on arrays, not jQuery objects, you need to use jQuery.makeArray to convert your selection into a true array first:
var position = $.map($.makeArray($('#ipadmenu > section')), function() {
return $(this).data('order');
} ); // position is now an indexed array
This approach is technically shorter than using .each, but I find it less clear.
Javascript:
var orders = [];
$('#ipadmenu > section').each(function() {
orders.push($(this).data('order'))
});
HTML:
<div id="ipadmenu">
<section data-order="1">1</section>
<section data-order="2">2</section>
</div>
You will want to do something like this:
// Get the elements and put them in an array
var position = $('#ipadmenu section').toArray();
console.log(position);
// Loop through the array
for (var i = 0; i < position.length; i++){
// Display the attribute value for each one
console.log("Section " + i + ": " + $(position[i]).attr('data-order'));
}
Working example here: http://jsfiddle.net/U6n8E/3/

Sorting Divs in jQuery by Custom Sort Order

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;

Categories

Resources