Fastest way to replace big drop down list in IE8 - javascript

I have to manage big drop down list (thousands of items) and I encounter performance problem with IE8 with the jQuery .html method.
Indeed it takes 3-4seconds to clear the content.
Do you have any workarounds ?
Code :
var selectHtml = "";
$(data.items).each(function () {
var option = "<option value='";
option += this.Value + "'";
if (this.Selected) {
option += " selected";
}
option += ">" + this.Text + "</option>";
selectHtml += option;
});
$(target).html(selectHtml);
.html of jQuery call .empty and in the IE profiler I can see that it is .empty that takes most of the time.

Assuming you mean something like
<ul id='mylist'>
<li>Item 1</li>
....
<li>Item n</li>
</ul>
or the equivalent select/option statement, you need:
$('#mylist').empty()
Alternatively, if you're only actually changing a few items in your dropdown list, perhaps you should maintain a map between the data.value and the element in the select list, so you only need to add items which have not already been placed in the list, and have a simple reference to items to remove.
I suspect you are wrong about the time split and most of the time is building the list. Try pushing all your new option items onto an array and then performing a single join of the array at the end.
var list = [];
$(data.items).each(function () {
var selected = this.Selected ? ' selected' : '';
var option = "<option value='" + this.Value + "'" + selected + ">"
+ this.Text + "</option>";
list.push( option);
});
$(target).html(list.join( "\n"));

I found the solution here : https://stackoverflow.com/a/23552898/1431524
I ended up with this code :
function clearOptions(select) {
var selectParentNode = select.parentNode;
if (selectParentNode) {
var newSelect = select.cloneNode(false); // Make a shallow copy
selectParentNode.replaceChild(newSelect, select);
return newSelect;
}
return undefined;
}
function appendSelectHtml(data, target) {
var selectHtml = [];
$(data.items).each(function () {
var selected = this.Selected ? ' selected' : '';
var option = "<option value='" + this.Value + "'" + selected + ">" + this.Text + "</option>";
selectHtml.push(option);
});
target = $(clearOptions(target[0])); //The item that was contained in the selector isn't in the DOM anymore
target.append(selectHtml.join(""));
}

Related

Razor JQuery Populate Drop Down from Model array

I am using a WebGrid to allow CRUD on my database (using MVC and EF entities). The grid works and filters they way I want it to. There are two columns that use dropdowns to display a value tied to another table (Projects and People) and these both work well for edits/ updates. I am using JQuery for an add new row and want the new row to have select fields like the grid does (so that the user can just find the person by name instead of having to enter the ID for example). I am referencing this post from another similar question, but when I implement the code I get a syntax error that I'm having trouble understanding.
Here is my scripting on the view side that shows my failed attempt. I'm creating an array from the project repository (Text is the name of the project and Value is the ID field), and populating it with the model values: Model.Projects, and then in the add row function I want to loop through the array to add in the options.
<script type="text/javascript">
var ProjectArray = new Array();
#foreach (var proj in Model.projects)
{
#:ProjectArray.push(Text: "#proj.Text", Value: "#proj.Value");
}
</script>
<script type="text/javascript">
$(function ()
{
$('body').on("click", ".add", function () {
var SelectedProject = "#Model.ProjectID";
var newRow = $('.save').length;
console.log('newRow = ' + newRow);
if (newRow == 0) {
var index = "new"+$("#meetingList tbody tr").length + 1;
var ProjectID = "ProjectID_" + index;
var Date = "Date_" + index;
var Attendees = "Attendees_" + index;
var Phase = "Phase_" + index;
var PeopleID = "PeopleID_" + index;
var Save = "Save _" + index;
var Cancel = "Cancel_" + index;
var tr = '<tr class="alternate-row"><td><span> <input id="' + ProjectID + '" type="select"/></span></td>' +
#* This is where I use the array to add the options to the select box*#
ProjectArray.forEach(function (item) {
if (item.Value == SelectedProject) { '<option selected="selected" value="' + item.Value + '">' + item.Text + '</option>' }
else { '<option value="' + item.Value + '">' + item.Text + '</option>' }
+
});
---remaining script omitted----
'<td><span> <input id="' + PeopleID + '" type="text" /></span></td>' +
'<td><span> <input id="' + Date + '" type="date" /></span></td>' +
'<td><span> <input id="' + Attendees + '" type="text" /></span></td>' +
'<td><span> <input id="' + Phase + '" type="text" /></span></td>' +
'<td> SaveCancel</td>' +
'</tr>';
console.log(tr);
$("#meetingList tbody").append(tr);
}
});
I am not sure how to parse the error, but the page source looks like this when creating my client side array:
var ProjectArray = new Array();
ProjectArray.push(Text: "Select Project", Value: ""); //<-- ERROR HERE:
ProjectArray.push(Text: "010111.00", Value: "74");
ProjectArray.push(Text: "013138.00", Value: "2");
So the model getting into the client side works (the text and value pairs are correct), but the error I get is for the first array.push line: missing ) after the argument list. I have played with moving this code block around, putting it in a separate <script> tag and the error likewise follows it around, always on the first array.push line. And regardless of where it is, the rest of my script functions no longer work. I think it must be something silly but I just am not seeing what I'm doing wrong.
The option list does not populate into something I can ever see, it just renders out on the page source as the javascript loop:
var tr = '<tr class="alternate-row"><td><span> <input id="' + ProjectID + '" type="select"/></span></td>' +
ProjectArray.forEach(function (item) {
if (item.Value == SelectedProject) { '<option selected="selected" value="' + item.Value + '">' + item.Text + '</option>' }
else { '<option value="' + item.Value + '">' + item.Text + '</option>' }
+
}); //-- Unexpected token here
And with the push array in its separate script block I get a second error that the last } is an unexpected token. This is some javascripting error I'm sure. But where it is an how to do this are beyond me right now.
I'm not used to javascript, and poor syntax leads to the vague errors I was getting. The first problem was fixed by adding the { . . . } around the array values. Then I created a function to create the arrays I need for people and projects as well as a function to take an array and create the option list to clean up the view code:
function createProjectArray() {
var ProjectArray = new Array();
#foreach (var proj in Model.projects)
{
if (proj.Value != "") {
#:ProjectArray.push({ Text: "#proj.Text", Value: "#proj.Value" });
}
}
return ProjectArray;
}
function createPeopleArray() {
var PeopleArray = new Array();
#foreach (var person in Model.people)
{
if (person.Value != "") {
#:PeopleArray.push({ Text: "#person.Text", Value: "#person.Value" });
}
}
return PeopleArray;
}
function SelectOptionsString(MyArray, SelectedValue) {
console.log(MyArray);
var OptionsList = "";
MyArray.forEach(item => {
if (item.Value == SelectedValue) { OptionsList += '<option
selected="selected" value="' + item.Value + '">' + item.Text + '</option>'; }
else { OptionsList += '<option value="' + item.Value + '">' + item.Text
+ '</option>'; }
})
return OptionsList;
}
Taking this approach allowed me to more easily parse the code and find the syntax errors. The Array.forEach syntax was an interesting hurdle, and this site helped me test out my syntax to eventually get it working as above.
So the server creates the javascript lines to create the array, and then I use the array to create my dropdown options list. This cleans up the add row function code nicely:
$('body').on("click",".addrow", function() {
var SelectedProject = "#Model.ProjectID";
var ProjectArray = createProjectArray();
var ProjectOptions = "";
ProjectOptions = SelectOptionsString(ProjectArray, SelectedProject);
var PeopleArray = createPeopleArray();
var PeopleOptions = "";
PeopleOptions = SelectOptionsString(PeopleArray, "");
var tr = '<tr class="alternate-row"><td><span> <select id="' +
ProjectID + '>' + ProjectOptions + '</select></span></td>' +
'<td><span> <select id="' + PeopleID + '>' + PeopleOptions +
'</select></span></td>' + '</tr>'
$("#myWebGrid tbody").append(tr);
});
And it also allows for some potential code reuse.

Get the value of a class when I check on a multiple options input

I want to get the value of team-id when I check it on a multiple options input and save this value on selectedTeams array
Common atributtes
teams: [],
selectedTeams: [],
Array teams get all teams on another function and works fine
I'm doing this function
GetSelectedTeams: function () {
var self = this;
var selected = $('input:checked').attr('team-id');
for (var i = 0; i < this.teams.length; i++) {
if (i == selected){
self.selectedTeams[i] = true;
}
}
var n = $('input:checked').length;
console.log("Selected Team is " + selected);
console.log("SelectedTeam length is " + self.selectedTeams.length);
console.log("Teams checked " + n);
}
The function trigger is
$('#teamsList').on('click','[id^=team]', function() {
this.GetSelectedTeams();
});
#teamsList is the list with all teams
<ul id="teamsList" style="list-style-type: none;">
And he will add with this structure that works fine
$('#teamsList').append('<li>\
<input type="checkbox" id="team' + item.Id + '" class="confirmCheckbox" team-id="' + item.Id + '" data-team-id="' + teamId + '" /> ' + item.teamName + '\
</li>\
');
Console shows the same team-id always, the lenght is always 2 and teams checked works fine.
You can get selected team id like this:
var selectedTeams=[];
$("input:checkbox").each(function(){
var $this = $(this);
if($this.is(":checked")){
selectedTeams.push($this.attr("data-team-id"));
}
});
here the variable selectedTeams will be having all teamid values for all of the checked checkboxes.

Current Alternative To .fontcolor() method in Javascript

I was given this task with some existing code to change the string color of each of three selector.value(s) that is output onto an input element to three different colors. The code boils the three selectors into a single output variable. Without destroying the code, I cannot figure out how to select each individual variables prior to condensing them.
If I could use the fontcolor() method, my life would be great but it's 2018 and I can't. Is there any way you can think of to solve this issue?To clarify, I need to alter the colors of the strings that belong to output(red), select1.value(blue) and select2.value(black.
Most of the action for this is happening in the parseOutput() function but I'm just stuck and don't think it's possible without rewriting the entire program.
function updateSelector(result){
var options = result.options;
var elementId = "select" + result.element;
var logger = document.getElementById('logger');
var selector = document.getElementById(elementId);
//logger.innerHTML = JSON.stringify(elementId);
selector.innerHTML = options;
selector.disabled = false;
}
google.script.run.withSuccessHandler(updateSelector).processOptions(0);
plate();
function resetAll(){
for (var i = 0;i<3;i++){
var selector = document.getElementById('select' + i);
selector.disabled = true;
selector.innerHTML = "";
}
google.script.run.withSuccessHandler(updateSelector).processOptions(0);
}
function finalSelection(){
var output = document.getElementById('out');
//output.focus();
output.select();
}
function plate(){
var plate = document.getElementById('plate');
plate.innerHTML = atob('Q3JhZnRlZCBieTogWmFjaGFyeSBTdGFjaG93aWFr');
}
//Adds the location as initial output, followed by divider, application, and issue if select1 is selected
//else statement added so if select0 is [Costco Website Name], to ommit the " - "
function parseOutput(){
var output = "";
if (select1.value.length > 0 && select0.value !== "[Costco Website Name]"){
output = output + ' - ' + select1.value + ' // ' + select2.value;
} else{
output = output + select1.value + ' // ' + select2.value;
}
out.value=output.trim();
}
And this is the Div that displays the output:
<div class="wide"><p><input class="wide" type="readonly" id="out" onfocus="this.select();"></p></div>
A modern replacement for fontcolor would use a span and a style (or class), e.g.:
function modernFontColor(str, color) {
return '<span style="color: ' + color + '">' + str + '</span>';
}
or
function modernFontClass(str, cls) {
return '<span class="' + cls + '">' + str + '</span>';
}
...where the class defines the styling.

sorting and appending li to ul asc

http://jsfiddle.net/Hms7Y/14/
the code above work well for inserting item according to 'level' without using any complex sorting algo, but there's a problem, when there is no ready markup, the level 2 will be still on top of level 1..
$(document).ready(function() {
$('button').click(function() {
var lvl = $('select').val();
var ref = $('li.level' + lvl).last();
var newLi = $('<li class="level'+ lvl + '">' + lvl + ' </li>');
console.log(ref);
(ref.length > 0) ? newLi.insertAfter(ref) : $("ul").append(newLi);
});
});
I have edited you fiddle to work as you expect: http://jsfiddle.net/Hms7Y/23/
Basically I have added this control on every click:
if($("ul").children().length>0){
$("li").each(function(){
if(lvl <= parseInt($(this).html())){
newLi.insertBefore($(this));
}else{
newLi.insertAfter($(this));
}
});
}else{
$("ul").append(newLi);
}

nextSibling returns null

I am getting an error saying that "this.nextSiblingElement.id is null" when clearly I have set the IDs for the elements as they generate. Which I admit still could use some work. I was just curious what is the cause of the nextSiblingElement's ID being returned as null. I made sure that the previous element was generated before the drop-down menu and that the menu is placed before it with previousChild.appendChild.
I can edit the code down if necessary but I was not sure how much to show for the sake of it making sense. I am only showing the relevant functions as follows:
//helper function to add elements to the form
function createNewFormElement(inputForm, feildName, elementName, elementValue, elementType){
var inputString = String(feildName + ": <input name='" + elementName + "' type='" + elementType + "' /> ");
var newElement = document.createElement('div');
newElement.style.display = 'inline';
if (feildName == "Quantity" && containerType != "Box/Bag"){
newElement.style.visibility = 'hidden';
}else{
newElement.style.visibility = 'visible';
}
if (feildName == "Quantity"){
newElement.id = "boxhide"+counter;
}else{
newElement.id = 'dynamicInput';
}
newElement.innerHTML = inputString;
newElement.value = elementValue;
document.getElementById(inputForm.id).parentNode.appendChild(newElement);
return newElement;
}
//helper function to add dropdown
function createNewDropDown(inputForm, feildName, elementName){
var dropDown, arValue;
var newElement = document.createElement('div');
newElement.id = 'dynamicInput';
newElement.style.display = 'inline';
dropDown = "<option value='" + containerType + "' selected>" + containerType + "</option>";
for (i=0; i<conArray.length; i++){
arValue = conArray[i];
dropDown = dropDown + "<option value='" + arValue + "'>" + arValue + "</option>";
}
var inputString = String(feildName + ": <select onchange='switchMain(this.nextElementSibling.id);' name='" + elementName + "' id='" + elementName + "'>" + dropDown + "</select> ");
newElement.innerHTML = inputString;
document.getElementById(inputForm.id).parentNode.lastChild.previousSibling.appendChild(newElement);
return newElement;
}
function getStyle(divName){
var temp = document.getElementById(divName).style.visibility;
return temp;
}
function switchMain(divName){
var e = document.getElementById("myContainers[]");
var strUser = e.options[e.selectedIndex].value;
if (strUser == "Box/Bag"){
var current = getStyle(divName);
//console.debug(current);
document.getElementById(divName).style.visibility = "visible";
}else{
document.getElementById(divName).style.visibility = "hidden";
}
}
You've got an inputString like
"… <select onchange='switchMain(this.nextElementSibling.id);' …>…</select> "
Now, if you execute
newElement.innerHTML = inputString;
You can't expect that the select element has any sibling elements - it's the only (element) node inside the newElement:
<div …> … <select onchange='switchMain(this.nextElementSibling.id);' …>…</select> </div>
Therefore, this.nextElementSibling will be null and you get an exception when trying to get its id property.
I made sure that the previous element was generated before the drop-down menu and that the menu is placed before it with previousChild.appendChild.
No, appendChild always inserts the new node in the end - regardless of the new parent's position in the DOM. If you want to insert an element in a special location, use insertBefore.

Categories

Resources