Chained select box fiddle
Chained select box and an Array.prototype function fiddle
I had a chained select box and an Array.prototype
function to combine two arrays into an associated array.They are for different use and unrelated. But when they are put together in the script, it gives me undefined is not a function
pointing to this part this.forEach
as the source of error. I ended up replacing the Array.prototype
function with this one (Example fiddle):
function associate(keys, values){
return keys.reduce(function (previous, key, index) {
previous[key] = values[index];
return previous
}, {})
}
I'm just curious why there is a conflict between the chained select box and the Array.prototype
function?
Here's the code:
$(document).ready(function(){
var data = [
{
"bigcat": "Sport",
"cat": "mainstream",
"choice": "football"
},
{
"bigcat": "Sport",
"cat": "mainstream",
"choice": "basketball"
},
{
"bigcat": "Sport",
"cat": "niche",
"choice": "MMA"
},
{
"bigcat": "Sport",
"cat": "niche",
"choice": "wrestling"
}
]
var $select = $('select');var $option="";
$.each(data, function (index, i) {
$option = $("<option/>").attr("value", i.choice).text(i.bigcat + "@" +( i.cat || "") +"@" + i.choice);
$select.append($option);
});
$select.dynamicDropdown({"delimiter":"@"});
});
Array.prototype.associate = function (keys) {
var result = {};
this.forEach(function (el, i) {
result[keys[i]] = el;
});
return result;
};
var animals = ['Cow', 'Pig', 'Dog', 'Cat'];
var sounds = ['Moo', 'Oink', 'Woof', 'Miao'];
console.dir(sounds.associate(animals));
Dynamic Drop down box Script
(function($) {
$.dynamicDropdown = {
/**
* Escape quotation marks and slashes
* @param {String} String to format
* @return {String}
*/
escapeQuotes : function(str) {
return str.replace(/([""\\])/g, "\\$1");
},
/**
* Build a <select> box from options
* @param {Array} Options
* @return {jQuery}
*/
buildSelectDropdown : function(options) {
var select = $(document.createElement("select"));
var option = null;
// Add options
for (var i in options) {
option = $(document.createElement("option"))
.val($.isArray(options[i]) ? i : options[i])
.html(i)
.appendTo(select);
}
return select;
}
};
$.fn.dynamicDropdown = function(options) {
var settings = {
"delimiter" : " ?",
"className" : "dynamic-dropdown"
};
$.extend(settings, options);
return $(this).each(function() {
/**
* Main dropdown (this)
* @type jQuery
*/
var mainDropdown = $(this);
/**
* Position of initial value of main dropdown
* @type Array
*/
var initialPosition = [];
/**
* Main array of all elements
* @type Array
*/
var data = [];
/**
* Array of all <select> boxes
* @type Array
*/
var selectElements = [];
/**
* Flag denoting whether the dropdown has been initialized
* @type Boolean
*/
var isInitialized = false;
/**
* Prepare a dropdown for use as a dynamic dropdown
* @param {jQuery|string} Dropdown
* @param {jQuery|HTMLElement} Sibling
* @param {Number} Level
* @param {Number} Position in the main array
* @return {jQuery}
*/
var prepareDropdown = function(dropdown, sibling, level, position) {
return $(dropdown)
.addClass(settings.className)
.data("level", level)
.data("position", position)
.insertAfter(sibling)
.each(buildDynamicDropdown)
.change(buildDynamicDropdown);
};
/**
* Initialize the dynamic dropdown <select> boxes
* @return {jQuery}
*/
var buildDynamicDropdown = function() {
var level = $(this).data("level") + 1;
var position = "";
// Get the position in the main data array
if (!isInitialized) {
for (var i = 0; i < level; i++) {
position += "[\"" + initialPosition[i] + "\"]";
}
} else {
position = $(this).data("position") + "[\"" + $.dynamicDropdown.escapeQuotes($(this).val()) + "\"]";
// Remove old <select> boxes
for (var i = selectElements.length; i > level; i--) {
selectElements.pop().remove();
}
}
var selectionOptions = eval("data" + position);
if ($.isArray(selectionOptions)) {
// Build the next dropdown
selectElements.push($.dynamicDropdown.buildSelectDropdown(selectionOptions));
if (!isInitialized) {
$(this).val(initialPosition[level - 1]);
}
prepareDropdown(selectElements[selectElements.length - 1], this, level, position);
} else if (!isInitialized) {
// Set the final value
$("option:contains('" + initialPosition[level - 1] + "')", selectElements[selectElements.length - 1]).attr("selected", "selected");
isInitialized = true;
} else {
// Set the value
mainDropdown.val($(this).val());
}
return $(this);
};
// Build the dynamic dropdown data
mainDropdown.children().each(function() {
var parts = $(this).html().split(settings.delimiter);
var name = "data";
var value = null;
// Set the initial position
if ($(this).is(":selected")) {
initialPosition = parts;
}
// Build the position of the current item
for (var i in parts) {
if(typeof parts[i] != "string") continue;
name += "[\"" + $.dynamicDropdown.escapeQuotes(parts[i]) + "\"]";
value = eval(name);
if (!value) {
// Set the level to have an empty array to be filled
eval(name + " = [];");
} else if (!$.isArray(value)) {
// Add data to the array
eval(name + " = [" + eval(name) + "];");
}
}
// Set the final index to have the value
eval(name + " = \"" + $(this).val() + "\";");
});
// Build the dynamic dropdown
selectElements[0] = $.dynamicDropdown.buildSelectDropdown(data);
prepareDropdown(selectElements[0], this, 0, "");
}).hide();
};
})(jQuery);
(function($)
{
$.fn.dynamicDropdown = function(options) {
var settings = {
"delimiter" : " » ",
"className" : "",
"levels" : [
{'markup':"{dd}",'class':false,'id':false,'disabled':false},
{'markup':"{dd}"}
]
};
$.extend(settings, options);
return $(this).each(function() {
//the original dropdown element
var mainDropdown = $(this);
var defaultSelection = false;
var levels = {};
//insert dropdown into markup, and finally place it in the DOM, attaching events, etc.
var insertSelectDropdown = function(dd, level, sibling, position){
var markup = settings.levels[level] && settings.levels[level].markup ? settings.levels[level].markup : '{dd}';
//to support markup both placing the dropdown within a container and without a container,
//its necessary to use a little silly dom magic
var container = $('<div>'+settings.levels[level].markup.replace('{dd}',$('<div></div>').append(dd.addClass('ddlevel-'+level)).html())+'</div>').children()['insert'+position](sibling);
var select = container.parent().find('select.ddlevel-'+level).removeClass('ddlevel-'+level);
if (settings.levels[level]['class']){
select.addClass(settings.levels[level]['class']);
}
if (settings.levels[level].id){
select.attr('id',settings.levels[level].id);
}
if (settings.levels[level].disabled){
select.prop('disabled','disabled');
}
return select.data('level',level).data('container',container).data('levels',dd.data('levels')).change(updateDropdowns);
}
//produce markup for select element
var buildSelectDropdown = function(options, selected) {
var select = $('<select></select>').data('levels',options);
// Add options
$.each(options,function(index,value){
var option = $('<option></option>').html(index);
if (typeof(value) != 'object'){
option.val(value);
}
if (selected && index == selected){
option.attr('selected','selected');
}
select.append(option);
});
return select;
};
//the event function that runs each time a select input value changes
var updateDropdowns = function(){
var current = $(this).children(':selected').html();
var options = $(this).data('levels')[current];
//a non-object means this is the end of the line, set the value
if (typeof(options) != 'object'){
mainDropdown.val($(this).val());
}
else {
//remove any dds after the one that just changed
var dd = $(this);
while (dd.data('next')){
dd = dd.data('next');
dd.data('container').detach();
}
var level = $(this).data('level') + 1;
//add new dds
$(this).data('next',insertSelectDropdown(buildSelectDropdown(options, defaultSelection[level]), level, $(this).data('container').last(), 'After').change());
}
};
//build levels from initial dropdown
mainDropdown.children().each(function() {
var options = $(this).html().split(settings.delimiter);
if ($(this).is(":selected")){
defaultSelection = options;
}
var level = levels;
for (var i=0; i < options.length; i++) {
if (!level[options[i]]){
//either an option is an object pointing to other objects/values,
//or some other type value, indicating that the user has made a selection
level[options[i]] = ((i+1)==options.length) ? $(this).val() : {};
}
level = level[options[i]];
}
});
//if no default selection, use first value
if (!defaultSelection){
defaultSelection = mainDropdown.children().first().html().split(settings.delimiter);
}
insertSelectDropdown(buildSelectDropdown(levels,defaultSelection[0]), 0, mainDropdown, 'Before').change();
//hide initial dropdown
}).hide();
};
})(jQuery);