javascript 基于 autocomplete/dropdown 菜单的单击事件未正确关闭

Click event on javascript based autocomplete/dropdown menu not closing properly

我创建了一个脚本,该脚本通过 hiding/showing 和 <ul> 模拟 <input type="text" /> 的自动完成,该 <ul> 与所述输入一起定位。一旦用户点击 <input type="text" /><ul> 将变为可见和可点击,并在满足以下三个条件之一时关闭:

到目前为止,一切正常。不幸的是,如果页面上有两个这样的 "autocomplete" 字段,我 运行 就会遇到问题。如果用户打开一个"autocomplete"界面,然后点击打开另一个界面,first/original界面没有按预期关闭。

我好像想不通哪里出错了。

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>

问题是,您希望 .autocomplete 元素在单击正文时隐藏

$(document.body).addEvent('click', function() {
    $$('.autocomplete ul').addClass('hidden');
});

您在 document.body 单击时添加了 'hidden' class,但是当您单击另一个 .autocomplete 输入时,事件不会冒泡到 body 元素,因为您使用此行故意阻止它在点击事件中传播

e.stopPropagation();

您可以通过向 click 事件添加条件来解决此问题,在该事件中隐藏所有其他 .autocomplete 元素,或者为 blur 事件添加另一个处理程序,在该事件中您隐藏元素本身。

这里的问题是,当你点击一个输入时,与其他输入绑定的事件不会被触发,因此 class 不会改变,你必须检查是否有任何在文档中打开的其他自动完成比你应该关闭它。你可以在你的点击事件中做一个小检查。

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 = '';
                    var allInput= document.getElementsByClassName(el.className);
                    for(var i =0 ;i<allInput.length; i++)
                    {
                       if(allInput[i].getParent().className=="autocomplete" && allInput[i] != el && allInput[i].getParent().getChildren('ul')[0].className != "hidden" )
                       {
                           allInput[i].getParent().getChildren('ul')  [0].className = 'hidden';
                       }
                    }
     
     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>

您可以使用 blur 事件。由于它在 click 之前触发,如果您需要移动支持,您可以将其更改为 mousedowntouchstart。所以你的 class 可能看起来像这样:

(注意我清理了一下,现在为每个元素创建了一个新的 Class 实例,我看到它更像 modular/independant)

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/

如果你需要触摸支持,你可以这样做:https://jsfiddle.net/1on4kpj0/1/