Click the event on the drop-down menu / javascript-based drop-down menu that does not close properly

advertisements

I created a script that simulates an autocomplete for an <input type="text" /> by hiding/showing a <ul> that is positioned with said input. Once the user clicks on the <input type="text" />, the <ul> will become visible and clickable, and will close when one of three conditions is met:

  • User reclicks on the <input type="text" />
  • User clicks on one of the options in the displayed <ul>
  • User clicks on anything that is not related to this system

Thus far, I've gotten everything to work just fine. Unfortunately, I've run into a problem if there are two such "autocomplete" fields on the page. If the user opens one "autocomplete" interface, then clicks to open the other interface, the first/original one does not close as expected.

I can't seem to figure out where I went wrong.

window.addEvent('domready', function() {
  new autoComplete();
});
var autoComplete = new Class({
    options: {
        version:        '1.0',
        lastUpdate: '2016-06-27'
    },

    Implements: [Options,Events],
    initialize: function(options) {
        this.setOptions(options);

        $$('.autocomplete').each(function(acl) {
            acl.getChildren('ul li').each(function(li) {
                li.addEvent('click', function() {
                    acl.getChildren('input[type=text]')[0].value = li.get('html');
                    acl.getChildren('input[type=hidden]')[0].value = li.get('data-id');
                    acl.getChildren('ul').addClass('hidden');
                });
            });

            acl.getChildren('input')[0].addEvents({
                click: function(e) {
                    e.preventDefault();
                    e.stopPropagation();

                    var el = e.target;
                    var val = el.value;
                    var aul = el.getParent().getChildren('ul')[0];
                    var str = '';

                    aul.toggleClass('hidden');

                    aul.getChildren().each(function(l) {
                        str = l.get('html').toLowerCase();
                        if (str.indexOf(val.toLowerCase()) != 0) {
                            l.addClass('hidden');
                        } else {
                            l.removeClass('hidden');
                        }
                    });
                },
                keyup: function(e) {
                    e.preventDefault();
                    e.stopPropagation();

                    var el = e.target;
                    var val = el.value;
                    var aul = el.getParent().getChildren('ul')[0];
                    var str = '';

                    aul.removeClass('hidden');

                    aul.getChildren().each(function(l) {
                        str = l.get('html').toLowerCase();
                        if (str.indexOf(val.toLowerCase()) != 0) {
                            l.addClass('hidden');
                        } else {
                            l.removeClass('hidden');
                        }
                    });
                }
            });
        });

        $(document.body).addEvent('click', function() {
            $$('.autocomplete ul').addClass('hidden');
        });
    }
});
html,
body {
  height: 100%;
}

.autocomplete {
  position: relative;
}

.autocomplete ul {
  list-style: none outside none;
  background-color: #FFF;
  margin: 0px;
  padding: 0px;
  position: absolute;
  top: 100;
  left: 0px;
  right: 0px;
  z-index: 500;
}

.autocomplete ul li {
  border-width: 0 1px 1px;
  border-style: solid;
  border-color: #000;
}

.autocomplete ul li:first-child {
  border-width: 1px;
}

.autocomplete ul li:hover {
  background-color: #CCC;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container-fluid">
  <div class="form-group">
    <label>Drk:</label>
    <div class="autocomplete">
      <input type="text" class="form-control" />
      <input type="hidden" />
      <ul class="hidden">
        <li data-id="drk1-Nito">Nito</li>
        <li data-id="drk1-Seath">Seath</li>
        <li data-id="drk1-FourKings">Four Kings</li>
        <li data-id="drk1-BedofChaos">Bed of Chaos</li>
        <li data-id="drk2-TheRotten">The Rotten</li>
        <li data-id="drk2-DukesDearFreja">Duke's Dear Freja</li>
        <li data-id="drk2-OldIronKing">Old Iron King</li>
        <li data-id="drk2-LostSinner">Lost Sinner</li>
        <li data-id="drk3-Yhorm">Yhorm</li>
        <li data-id="drk3-Aldritch">Aldritch</li>
        <li data-id="drk3-Abysswatcher">Abyss Watcher</li>
        <li data-id="drk3-Lothric">Lothric</li>
      </ul>
    </div>
  </div>
  <div class="form-group">
    <label>Bld:</label>
    <div class="autocomplete">
      <input type="text" class="form-control" />
      <input type="hidden" />
      <ul class="hidden">
        <li data-id="bld-ClericBeast">Cleric Beast</li>
        <li data-id="bld-FatherGascoigne">Father Gascoigne</li>
        <li data-id="bld-WitchesofHemwick">Witches of Hemwick</li>
        <li data-id="bld-VicarAmelia">Vicar Amelia</li>
        <li data-id="bld-ShadowsofYarhnam">Shadows of Yarhnam</li>
        <li data-id="bld-VacuousRom">Vacuous Rom</li>
        <li data-id="bld-TheOneReborn">The One Reborn</li>
        <li data-id="bld-Micolash">Micolash</li>
        <li data-id="bld-MergosWetnurse">Mergo's Wetnurse</li>
        <li data-id="bld-OldHunterGermaine">Old Hunter Germaine</li>
        <li data-id="bld-MoonPresence">Moon Presence</li>
      </ul>
    </div>
  </div>
</div>

You can use the blur event. Since it fires before click you could change that to mousedown and touchstart if you need mobile support. So your class could look like this:

(notice I cleaned up a bit and create now a new Class instance to each element, I see it more modular/independant like that)

window.addEvent('domready', function() {
    $$('.autocomplete').each(function(el) {
        new autoComplete(el);
    });
});
var autoComplete = new Class({
    options: {
        version: '1.0',
        lastUpdate: '2016-06-27'
    },
    Implements: [Options, Events],
    initialize: function(acl, options) {
        this.setOptions(options);
        var self = this;
        var ul = acl.getElement('ul');
        var lis = ul.getChildren('li');
        var input = acl.getElement('input');

        lis.addEvent('mousedown', function(e) {
            input.value = this.get('html');
            acl.getElement('input[type=hidden]').value = this.get('data-id');
            ul.addClass('hidden');
        });

        input.addEvents({
            mousedown: function(e) {
                ul.toggleClass('hidden');
                self.toggle(lis, this.value.toLowerCase());
            },
            keyup: function(e) {
                ul.removeClass('hidden');
                self.toggle(lis, this.value.toLowerCase());
            },
            blur: function(e) {
                ul.addClass('hidden');
            }
        });
    },
    toggle: function(els, val) {
        els.each(function(el) {
            var str = el.get('html').toLowerCase();
            var match = str.indexOf(val) != -1;
            el.toggleClass('hidden', !match);
        });
    }
});

jsFiddle: https://jsfiddle.net/1on4kpj0/

If you need touch support you could do like this: https://jsfiddle.net/1on4kpj0/1/