自定义下拉焦点:超出调用堆栈大小
Custom dropdown focus: call stack size exceeded
我正在使用 jquery (slim) 构建我自己的下拉插件。下拉元素本身是 div 和 tabindex="0"
。
我希望下拉菜单与浏览器的焦点状态一起工作:当元素获得焦点时打开下拉菜单,当元素失去焦点时关闭它。目前我收到以下错误:
jquery.slim.min.js:2 Uncaught RangeError: Maximum call stack size
exceeded
代码如下所示(为了便于阅读删除了部分,标记了问题):
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
instance.open();
e.preventDefault();
}).focusout(function() {
instance.close();
}).mousedown(function() {
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* @param state
* @returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* @param callback
* @param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
我知道我正在触发一个(无穷无尽的)递归函数,但我不确定如何解决这个问题。
感谢所有帮助!
编辑:
固定
;(function($, window, document) {
'use strict';
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
console.log('opening');
instance.open();
e.preventDefault();
}).focusout(function() {
console.log('closing');
instance.close();
}).mousedown(function() {
console.log('toggling');
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* @param state
* @returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
empty: function() {
return $element.hasClass('dropdown--empty');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Toggles the dropdown.
*/
toggle: function() {
if (this.is('open')) this.close();
else this.open();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* @param callback
* @param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
// Plugin definition
$.fn.dropdown = function(options, args) {
return this.each(function() {
if ( ! $ .data(this, plugin)) {
$.data(this, plugin, new Dropdown(this, options));
}
});
};
})(jQuery, window, document);
$('.dropdown').dropdown();
.dropdown {
position: relative;
display: block;
padding: .625rem .8125rem;
padding-right: 2rem;
font-size: 16px;
color: #333;
line-height: 1.125;
outline: 0;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.dropdown.dropdown--open .dropdown__menu {
display: block;
}
.dropdown__menu {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="dropdown" tabindex="0">
<span>Favorite animal</span>
<ul class="dropdown__menu" tabindex="-1">
<li class="dropdown__item">Cats</li>
<li class="dropdown__item">Dogs</li>
<li class="dropdown__item">Monkeys</li>
<li class="dropdown__item">Elephants</li>
</ul>
</div>
我正在使用 jquery (slim) 构建我自己的下拉插件。下拉元素本身是 div 和 tabindex="0"
。
我希望下拉菜单与浏览器的焦点状态一起工作:当元素获得焦点时打开下拉菜单,当元素失去焦点时关闭它。目前我收到以下错误:
jquery.slim.min.js:2 Uncaught RangeError: Maximum call stack size exceeded
代码如下所示(为了便于阅读删除了部分,标记了问题):
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
instance.open();
e.preventDefault();
}).focusout(function() {
instance.close();
}).mousedown(function() {
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* @param state
* @returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* @param callback
* @param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
我知道我正在触发一个(无穷无尽的)递归函数,但我不确定如何解决这个问题。
感谢所有帮助!
编辑: 固定
;(function($, window, document) {
'use strict';
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
console.log('opening');
instance.open();
e.preventDefault();
}).focusout(function() {
console.log('closing');
instance.close();
}).mousedown(function() {
console.log('toggling');
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* @param state
* @returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
empty: function() {
return $element.hasClass('dropdown--empty');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Toggles the dropdown.
*/
toggle: function() {
if (this.is('open')) this.close();
else this.open();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* @param callback
* @param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
// Plugin definition
$.fn.dropdown = function(options, args) {
return this.each(function() {
if ( ! $ .data(this, plugin)) {
$.data(this, plugin, new Dropdown(this, options));
}
});
};
})(jQuery, window, document);
$('.dropdown').dropdown();
.dropdown {
position: relative;
display: block;
padding: .625rem .8125rem;
padding-right: 2rem;
font-size: 16px;
color: #333;
line-height: 1.125;
outline: 0;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.dropdown.dropdown--open .dropdown__menu {
display: block;
}
.dropdown__menu {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="dropdown" tabindex="0">
<span>Favorite animal</span>
<ul class="dropdown__menu" tabindex="-1">
<li class="dropdown__item">Cats</li>
<li class="dropdown__item">Dogs</li>
<li class="dropdown__item">Monkeys</li>
<li class="dropdown__item">Elephants</li>
</ul>
</div>