0

I am trying to create a drop down list of users on a website and, if those users are past their subscription date, they're red in the drop down list. This works fine.

function SetDropDownColors(itemArray, dropDownList) { $('#' + dropDownList).each(function () {

    for (var id in itemArray) {

        //should the user be found in the retired array, make them red so they cannot be selected by the admin
        if ($.inArray(itemArray[id].Value, retiredUsers[0]) >= 0) {

            $("option[value=" + "'" + itemArray[id].Value + "'" + "]").css({ 'font-style': 'italic', 'color': 'red' });
        }
    }        
});

}

However, when the admin selects a user that's expired, the text turns back to black and the font to normal (instead of italic).

Why does that happen, and how can I change it to be what it was prior to being selected?

I've tried many things, but here is the latest; several drop downs have the actionCreator class, so I key off the event. someFuntion returns bool and the console has the 'hit', so I know it makes it that far.

I can get everything to turn red/italic by making $(this).css red and italic but that isn't what's needed. Not every user should appear red, if they have a good subscription they should appear black in the drop down list.

Now, moving to the selected option, I have hit part:

function setSelectedValueColors() {

$('.wds-red').each(function (index, value) {

    var person = $(this).val();
    var selectValue;

    if (person) {

        selectValue = person.trim();
    }

    if ($.inArray(selectValue, retiredUsers[0]) >= 0) {

        console.log('I think this should be red: ', selectValue)

        $(this).css('font-style', 'italic');
        $(this).css('color', 'red');
        $(this).children().css('font-style', 'normal');
    }

    else {

        console.log('I think this should be black: ', selectValue)

        $(this).css('font-style', 'normal');
        $(this).css('color', 'black');
        $(this).children().css('font-style', 'normal');
    }
});

}

This will not retain and make the text red for ONLY the selected value; all values in the drop down list appear red.

I've revised this question to my revised approach.

Any help is greatly appreciated.

Thanks,

B

cyimxtck
  • 21
  • 3
  • *~However, when the admin selects a user that's expired, the text turns back to black and the font to normal (instead of italic)."* If this is IRL, then the status of being "expired" could be indefinite. So, this happens after refreshing the page (which is normal behavior)? – zer00ne Mar 10 '22 at 03:18
  • No, this happens when the drop down list is selecting that value. The value is red in the drop down list rows, but after it's selected, the value turns black when it becomes the "selected value" – cyimxtck Mar 10 '22 at 10:08
  • You can see it here: https://option-colors.bmyers1.repl.co/ – cyimxtck Mar 20 '22 at 18:15
  • What are you testing with? PC/Mac? Browser? I just tested it with Win11 Chrome and Firefox and the selected text is still red. – zer00ne Mar 20 '22 at 18:22
  • OIC, Ok I understand, review my [answer](https://stackoverflow.com/a/71552870/2813224) – zer00ne Mar 21 '22 at 05:05

1 Answers1

0

The jQuery on https://option-colors.bmyers1.repl.co/ is very bloated. So I rewrote the whole page and resolved the issue as well.

Problem

The <option> tag is impossible to style reliably because it's styles are OS and browser dependent, so what may work for Windows Chrome probably won't work for Mac Chrome and so on and so on. Aside from using plugins that just replace <select> and <option>s with <div>s the <select> itself can be styled but all of it's <option>s inherit the styles, so for instance when clicking the <select> with red text all of the <option>s will have red text which is probably confusing to the user.

Solution

What we can do is to bind event handlers to the "change" and "mousedown" events. Let's look at the "change" event first:

//      ↙️<form id='agents'>   ↙️this
$('#agents').on('change', 'select', function() {...

The "change" event applies only to <form> and form controls. When a user enters data into a form control and then unfocuses by clicking somewhere else or by tabbing, the "change" event is triggered. In the example, the "change event is registered to the <form>. Note the second parameter is 'select' which designates that the current <select> the user has clicked or tabbed over to is considered this. Next part:

if (retired.includes(this.value)) {
  $(this).addClass('expired');
} else {
  $(this).removeClass('expired');
} 
...

Basically, it searches the retired array and checks to see if there's a match with the current value of this (the <select> that was clicked), using the String method .includes(). If there is a match, the .expired class is added to this otherwise it's removed. The last part of the "change" handler ties in with the "mousedown" handler (which will be expained afterwards):

  $(this).removeClass('active');
});

Note, in the <style> block there's 2 classes: .expired and .active. Since .active proceeds .expired, the styles of .active overrides the styles of .expired. So when there is a "change" event the .active class (which it gets from clicking the <select>, explained next) is removed ensuring that the .expired class shows (if .expired was added). Last is the "mousedown" handler:

$('#agents').on('mousedown', 'select', function() {
  $(this).addClass('active');
});

Whenever a <select> is clicked, will add the .active class which overrides the .expired class. Once the <select> is no longer focused, the <option>s which currently have black text will revert back to red text if .expired is applied already.

// Filter array of "id" values 
const retired = ["VKG5530", "XQVD542", "JNR3959"];

/* 
Typical JSON file converted into a JavaScript 
object literal
*/
const agents=[{id:"KWHR604",name:"Sherwin Kloisner",dept:"wholesale"},{id:"VKG5530",name:"Haily Preene",dept:"distribution"},{id:"TPQI605",name:"Magdaia Losseljong",dept:"retail"},{id:"DNTH620",name:"Bing Shyres",dept:"wholesale"},{id:"BYWD417",name:"Karla Antushev",dept:"distribution"},{id:"UNSR006",name:"Lanie Howship",dept:"retail"},{id:"OZJE200",name:"Lacy Erridge",dept:"wholesale"},{id:"YONE314",name:"Rivalee MacQuarrie",dept:"distribution"},{id:"JNR3959",name:"Hedwiga Hansley",dept:"retail"},{id:"EFGS324",name:"Ferdy Biesinger",dept:"wholesale"},{id:"WTJK762",name:"Tamqrah McCoole",dept:"distribution"},{id:"IQNW935",name:"Consuelo Roantree",dept:"retail"},{id:"XQVD542",name:"Roger Briscow",dept:"wholesale"},{id:"OCHV367",name:"Franky Fassbender",dept:"distribution"},{id:"DYH4861",name:"Pamela Juris",dept:"retail"}];
/** @function
 * @name fillDD
 * @description - Populates select tags with
 * option tags that represent data objects from
 * an object array.
 * @param {array<object>} json - Example of each
 * object within array:
 *  { id: "ABC*123", name: "Jon Doe", dept: 
 *    "wholesale", "distribution", or "retail" } 
 *    *number or uppercase letter.
 */
function fillDD(json) {
  json.forEach(agent => {
      const option = makeOpt(agent);
      $('#'+agent.dept).append(option);
  });
};
    
/** @function
 * @name makeOpt
 * @description - Makes an option tag for each
 * data object
 * @param {object} agent - An object that 
 * represents an agent
 * @returns {element} - An option tag with agent
 * data
 */
function makeOpt(agent) {
  const opt = document.createElement('option');
  opt.value = agent.id;
  opt.textContent = agent.name;
  return opt;
};
    
/*
There are 2 event handlers registered to 
form#agents. When the change event happens on 
a select, it's value is compared vs retired
array. If there's a match, the .expired class 
is added and if it had .active class it would 
be removed.
*/
$('#agents').on('change', 'select', function() {
  if (retired.includes(this.value)) {
    $(this).addClass('expired');
  } else {
    $(this).removeClass('expired');
  }
  $(this).removeClass('active');
});
    
/*
When a mousedown event fires on a select,
.active class is added 
*/
$('#agents').on('mousedown', 'select', function() {
  $(this).addClass('active');
});

fillDD(agents);
html {font: 2ch/1.15 'Segoe UI'}
label, select {display: block;font: inherit;}
.expired {color: red;font-style: italic}
.active {color: black;font-style: normal;}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>WDR - Wholesale, Distribution, and Retail Agents</title>
  <link href="style.css" rel="stylesheet" type="text/css">
  <style>/* Inline styles can go here */</style>
   </head>
   <body>
 <form id='agents'>
   <fieldset>
     <legend>WDR Agents</legend>
     <label for='wholesale'>Wholesalers</label>
     <select id='wholesale' name='wholesalers'>
       <option selected disabled>Select an agent</option>
     </select>
       <label for='distribution'>Distributors</label>
       <select id='distribution' name='distributors'>
         <option selected disabled>Select an agent</option>
        </select>
        <label for='retail'>Retailers</label>
        <select id='retail' name='retailers'>
          <option selected disabled>Select an agent</option>
         </select>
       </fieldset>
     </form>
     <script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>/* Place script before the </body> end
tag. This way it will be rendered after the DOM 
has been rendered. Also, $(document).ready(...
won't be needed if all the script is located 
here. */</script>
  </body>

</html>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • This is great content, hands down, thanks for that. The only things I see that I want to have function differently is the list of names, when they load and the drop down is clicked, have no red names in them until the expired one is clicked. Is there a way to make the list have the red names prior to clicking them so the admin knows what "not to click"? Also, the loading of the names, some are selected already since they are assigned. Those have to be red as well. – cyimxtck Mar 21 '22 at 11:03
  • We are using Chrome on PCs across the board; this is our corporate standard. Thanks. – cyimxtck Mar 21 '22 at 11:07
  • Unfortunately, the options can only inherit text color from the select, so it's either all black or all red. In order to have such granular control you'll need to use a plugin (easy), use alternate elements and a lot of tricks to make it appear like a select (moderate), or create a custom element (difficult). If you'd like to try the second option review my answer [here](https://stackoverflow.com/a/53759295/2813224) and vote for it if it resolves your issue. – zer00ne Mar 21 '22 at 11:27