0

How do I filter the text, "positivity" preserving parents and children. Note that the parents and children are in ascending order. Example if you have 'class child-1' is a child child-2 is another child and so on. When you have a child-0 class followed by another child-0, the first child-0 class has no children. Pure javascript please. Thanks in advance.

    <!DOCTYPE html>
    <html lang="pt-BR">
        <head>
            <meta charset="UTF-8">   
        </head>
        <body>
            <form>
                <input type="text" id="searchFilter" name="searchFilter" placeholder="Search" 
                       onkeyup="filterItems(this);">
                <select id="help-my-brother" name="ddVehicles" size="4">
                    <option value="-1">None</option>
                    <option class="child-0" value="1">United States</option>
                    <option class="child-0" value="2">Australia</option>
                    <option class="child-0" value="3">Brazil</option>
                    <option class="child-1" value="4">&nbsp;&nbsp;&nbsp;Iceland</option>
                    <option class="child-2" value="5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UK</option>
                    <option class="child-0" value="6">Germany</option>
                    <option class="child-1" value="7">&nbsp;&nbsp;&nbsp;Italy</option>
                    <option class="child-2" value="8">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;France</option>
                    <option class="child-3" value="9">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;China</option>
                    <option class="child-4" value="10">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                    <option class="child-0" value="11">Japan</option>
                    <option class="child-0" value="12">Argentina</option>
                    <option class="child-0" value="13">Colombia</option>
                    <option class="child-0" value="14">Mexico</option> 
                    <option class="child-0" value="15">Chile</option>
                    <option class="child-0" value="16">Venezuela</option>
                    <option class="child-0" value="17">Peace and love</option>
                    <option class="child-0" value="18">Florida</option>
                    <option class="child-1" value="19">&nbsp;&nbsp;&nbsp;Hug</option>
                    <option class="child-2" value="20">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Action</option>
                    <option class="child-3" value="21">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Adaptable</option>
                    <option class="child-4" value="22">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                    <option class="child-0" value="23">Love</option>
                    <option class="child-1" value="24">&nbsp;&nbsp;&nbsp;charisma</option>
                    <option class="child-2" value="25">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Charm</option>
                    <option class="child-3" value="26">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                    <option class="child-4" value="27">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Friend</option>
                    <option class="child-0" value="28">Peru</option>
                    <option class="child-1" value="29">&nbsp;&nbsp;&nbsp;clarity</option>
                    <option class="child-0" value="30">collaboration</option>
                    <option class="child-0" value="31">companionship</option>
                    <option class="child-0" value="32">Communication</option>
                    <option class="child-0" value="33">reliable</option>
                </select>
            </form>
    
            <script>
    
                var optionsCache = [];
    
                function filterItems(el) {
                    var value = el.value.toLowerCase();
                    var form = el.form;
                    var opt, sel = form.ddVehicles;
                    if (value == '') {
                        restoreOptions();
                    } else {
                        // Loop backwards through options as removing them modifies the next
                        // to be visited if go forwards
                        for (var i = sel.options.length - 1; i >= 0; i--) {
                            opt = sel.options[i];
                            if (opt.text.toLowerCase().indexOf(value) == -1) {
                                sel.removeChild(opt)
                            }
                        }
                    }
                }
    
    // Restore select to original state
                function restoreOptions() {
                    var sel = document.getElementById('help-my-brother');
                    sel.options.length = 0;
                    for (var i = 0, iLen = optionsCache.length; i < iLen; i++) {
                        sel.appendChild(optionsCache[i]);
                    }
                }
    
    
                window.onload = function () {
                    // Load cache
                    var sel = document.getElementById('help-my-brother');
                    for (var i = 0, iLen = sel.options.length; i < iLen; i++) {
                        optionsCache.push(sel.options[i]);
                    }
                }
    
            </script>
        </body>
    </html>

The result in the search for "positivity" has to be this:

<form>
            <input type="text" id="searchFilter" name="searchFilter" placeholder="Search" 
                   onkeyup="filterItems(this);">
            <select id="help-my-brother" name="ddVehicles" size="4">
                <option class="child-0" value="6">Germany</option>
                <option class="child-1" value="7">&nbsp;&nbsp;&nbsp;Italy</option>
                <option class="child-2" value="8">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;France</option>
                <option class="child-3" value="9">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;China</option>
                <option class="child-4" value="10">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                <option class="child-0" value="18">Florida</option>
                <option class="child-1" value="19">&nbsp;&nbsp;&nbsp;Hug</option>
                <option class="child-2" value="20">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Action</option>
                <option class="child-3" value="21">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Adaptable</option>
                <option class="child-4" value="22">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                <option class="child-0" value="23">Love</option>
                <option class="child-1" value="24">&nbsp;&nbsp;&nbsp;charisma</option>
                <option class="child-2" value="25">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Charm</option>
                <option class="child-3" value="26">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
                <option class="child-4" value="27">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Friend</option>
            </select>
        </form>
IT goldman
  • 14,885
  • 2
  • 14
  • 28
  • 1
    The best solution would be to modify your html to make filtering easier. The standard solution would use [optgroup](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup) elements to group associated items. Yet, if that's not possible then use class names that indicate the relationships, e.g., child-1-2 where 1 is the group and 2 the child index. With these class names you could then [select by prefix](https://stackoverflow.com/questions/3338680/is-there-a-css-selector-by-class-prefix) – Yogi Oct 05 '22 at 22:26

1 Answers1

0

It's not exactly clear to me what you want to do once you've classified each option element, but at any rate...

You can use the function findMatchingOptions below to classify each option: Provide an array of the options elements to it, and it will return an array of objects of type:

{
  element: HTMLElement;
  isValid: boolean;
}

You can use the isValid property to do whatever you need to do (e.g. hide the invalid ones). I've enumerated the results in the console output for demonstration.

<form>
  <input type="text" id="searchFilter" name="searchFilter" placeholder="Search" onkeyup="filterItems(this);">
  <select id="help-my-brother" name="ddVehicles" size="4">
    <option value="-1">None</option>
    <option class="child-0" value="1">United States</option>
    <option class="child-0" value="2">Australia</option>
    <option class="child-0" value="3">Brazil</option>
    <option class="child-1" value="4">&nbsp;&nbsp;&nbsp;Iceland</option>
    <option class="child-2" value="5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UK</option>
    <option class="child-0" value="6">Germany</option>
    <option class="child-1" value="7">&nbsp;&nbsp;&nbsp;Italy</option>
    <option class="child-2" value="8">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;France</option>
    <option class="child-3" value="9">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;China</option>
    <option class="child-4" value="10">
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
    <option class="child-0" value="11">Japan</option>
    <option class="child-0" value="12">Argentina</option>
    <option class="child-0" value="13">Colombia</option>
    <option class="child-0" value="14">Mexico</option>
    <option class="child-0" value="15">Chile</option>
    <option class="child-0" value="16">Venezuela</option>
    <option class="child-0" value="17">Peace and love</option>
    <option class="child-0" value="18">Florida</option>
    <option class="child-1" value="19">&nbsp;&nbsp;&nbsp;Hug</option>
    <option class="child-2" value="20">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Action</option>
    <option class="child-3" value="21">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Adaptable</option>
    <option class="child-4" value="22">
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
    <option class="child-0" value="23">Love</option>
    <option class="child-1" value="24">&nbsp;&nbsp;&nbsp;charisma</option>
    <option class="child-2" value="25">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Charm</option>
    <option class="child-3" value="26">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;positivity</option>
    <option class="child-4" value="27">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Friend
    </option>
    <option class="child-0" value="28">Peru</option>
    <option class="child-1" value="29">&nbsp;&nbsp;&nbsp;clarity</option>
    <option class="child-0" value="30">collaboration</option>
    <option class="child-0" value="31">companionship</option>
    <option class="child-0" value="32">Communication</option>
    <option class="child-0" value="33">reliable</option>
  </select>
</form>

<script type="module">

const childClassRegexp = /^child-(\d+)$/;

/**
 * @param {HTMLElement} element
 * @returns {number | undefined}
 */
function findChildNumber (element) {
  const className = [...element.classList].find(
    className => childClassRegexp.test(className)
  );
  return className ? Number(className.slice(6)) : undefined;
}

/**
 * @param {Array<HTMLElement>} elements
 * @param {string | undefined} filterTextContent
 * @returns {Array<{ element: HTMLElement; isValid: boolean }>}
 */
function findMatchingOptions (elements, filterTextContent = 'positivity') {
  /** @type {Array<{ element: HTMLElement; isValid: boolean }>} */
  const results = [];
  
  /** @type {Array<HTMLElement>} */
  const candidates = [];
  let previousChildNumber = -1;

  const evaluateCandidates = () => {
    const isValid = candidates
      .some(element => element.textContent?.includes(filterTextContent));
    for (const element of candidates) results.push({element, isValid});
    candidates.length = 0;
  };

  for (const element of elements) {
    const childNumber = findChildNumber(element);

    if (childNumber !== previousChildNumber + 1) {
      evaluateCandidates();
      previousChildNumber = -1;

      if (typeof childNumber !== 'number') {
        results.push({element, isValid: false});
        continue;
      }
    }

    candidates.push(element);
    previousChildNumber += 1;
  }

  evaluateCandidates();
  return results;
}

function main () {
  const optionElements =
    [...document.querySelectorAll('select#help-my-brother > option')];

  const results = findMatchingOptions(optionElements);

  for (const {element, isValid} of results) {
    element.hidden = !isValid; // Hide the invalid ones

    console.log(
      isValid ? '✅' : '❌',
      findChildNumber(element),
      element.textContent.trim(),
    );
  }
}

main();

</script>
jsejcksn
  • 27,667
  • 4
  • 38
  • 62