So I wrote a jquery plugin that turns select elements into a styleable div and ul with the same functionality. I was previous calling each select element individually, like so:
$("#myElementId").selectList();
But now I’ve decided that I want to be able to add the “selectList” class to a select list and then loop over all of the elements with the selectList class and turn them into the dynamic element, like so:
var selects = $(".selectList");
for (i=0,j=selects.length;i<j;i++){
$(selects[i]).selectList();
}
Which works fine, but only for the first element. For some reason, once .selectList() gets called, it ends my for loop and it never proceeds to the next iteration. I have no idea why. I have verified that $(".selectList") is indeed longer than 1, and if I put nothing in the loop except console.log(selects[i]) I get a log of each element as expected. It’s not until I call my extension on one of them that it stops the loop.
Can somebody please explain this behavior to me?
Here’s my plugin code if you need it. Please do not redistribute or reuse with crediting me in the comments of the js file:
// Custom select element functionality.
jQuery.fn.selectList = function(){
// Grab the original select element and it's options
var originalSelect = this;
var originalOptions = originalSelect.children("option");
var originalId = originalSelect.attr("id");
// Set the original select item to be hidden
originalSelect.css({"display": "none !important"});
// Set up our replacement block
var newContainer = "<div class='selectList_container' id='" + originalId + "_selectList' tabindex='0'>" +
"<p class='selectList_value'>" + $(originalOptions[0]).html() + "</p>" +
"<div class='selectList_optionsWrapper' style='height: 0 !important'>" +
"<ul class='selectList_options' style='display: none;'></ul>" +
"</div>" +
"</div>";
// Append it to the original container
$(newContainer).insertAfter(originalSelect);
// Set up our new variables. All references from here on in the script will reference the existing element already added to the document.
newContainer = $("#" + originalId + "_selectList");
var newValue = newContainer.find(".selectList_value");
var newList = newContainer.find(".selectList_options");
var newItems;
// Function to toggle the list
var toggleList = function(){
newList.toggle();
};
// Create our options and add them to our new list
for (i=0, j=originalOptions.length; i<j; i++){
// Get original option values
var thisOption = $(originalOptions[i]);
var optionText = thisOption.html();
var optionValue = thisOption.val();
// Build out our new item
var newItem = "<li name='" + optionValue + "'";
// Add a class of first and last to the first and last options to aid in styling
if (i == 0) {
newItem += " class='first' ";
} else if (i == (j-1)){
newItem += " class='last' ";
};
// Close out our new item
newItem += ">" + optionText + "</li>";
// Add our new item to the newItems string to be added to the dom later
newItems += newItem;
};
// Add our new item to the list we've made
$(newItems).appendTo(newList);
// Reference to the new list items
var newOptions = $("#" + originalId + "_selectList .selectList_options li");
// Default CSS values for our new dom elements
newContainer.css({position: "relative"});
newValue.css({cursor: "pointer", display: "block", "text-align": "left"});
newList.css({margin: "0 auto", "z-index": "9999999"});
newOptions.css({cursor: "pointer", display: 'block'})
// Opens and closes the new select list when the select value is clicked on
newValue.click(toggleList);
// Closes the list if the select element loses focus (mimics default select element behavior).
// In order for this to work, the element the blur event listener is being bound to must have the attribute tabIndex=0.
// This tricks the browser into thinking it is a form element, otherwise blur will not fire.
newContainer.blur(function(){
// This is in a setTimeout function because of the way IE detects the blur.
// 150ms is seamless to the end user but upholds the functionality in IE.
window.setTimeout(function hide_list(){
if (newList.is(":visible")) {
toggleList();
};
},150);
});
// Changes the value of our new select value and activates the old select option when one of our new list items is clicked
for (i=0, j=newOptions.length; i<j; i++) {
$(newOptions[i]).click(function(){
var thisText = $(this).html();
var thisValue = $(this).attr("name");
newValue.html(thisText).attr({"name": thisValue});
newList.toggle();
originalSelect.val($(this).attr("name")).trigger('change');
});
};
// Make sure if the original select list changes without the use of our new elements, that the new elements stay updated.
this.change(function(){
newValue.html($(this).find("option:selected").text());
});
};
How abt using $.each where the current context using this should help..
Might be converting it to a DOM element selects[i] seems to be creating the problem..