如何确定给定 div 中的某些内容是否具有焦点?

How to find out if something in a given div has focus?

使用 angularjs,我正在显示这样的 2 级列表

- first main item
  - first subitem of the first main item
  - second subitem of the first main item
  - AN EMPTY ITEM AS PLACEHOLDER TO ENTER THE NEXT SUBITEM
- second main item
  - first subitem of the second main item
  - second subitem of the second main item
  - AN EMPTY ITEM AS PLACEHOLDER TO ENTER THE NEXT SUBITEM

为了节省位置,我想仅在相应 div 中的任何内容具有焦点时才显示 PLACEHOLDER,因此只有一个这样的占位符。我知道有 ngFocus,但我更喜欢比创建大量事件处理程序更简单的方法。也许是这样的:

<div ng-focus-model="mainItem.hasFocus" ng-repeat="mainItem in list">
   ... main item line
   ... all subitems
</div>

单向绑定就足够了,因为我不需要设置焦点。

不幸的是,唯一可靠的方法是响应输入中的 focus\blur 事件...这是获得通知的唯一方法。

您可以将隐藏的输入作为每个 div 中的第一个元素并在其上放置 NgFocus 属性,但这仅在用户按 Tab 键进入时才有效。

这里的问题如下;我们希望避免为每个 child 添加事件侦听器,而是仅将其添加到 parent。 parent 将负责采取适当的行动。对此的一般解决方案是使用均匀传播(委托)。我们只将一个侦听器附加到 parent,当 child 上发生事件时(在本例中关注 input 元素),它将冒泡到 parent 和 parent 将执行侦听器。

指令如下:

app.directive('ngFocusModel', function () {
    return function (scope, element) {

      var focusListener = function () {
          scope.hasFocus = true;
          scope.$digest();
      };

      var blurListener = function () {
          scope.hasFocus = false;
          scope.$digest();
      };


      element[0].addEventListener('focus', focusListener, true);
      element[0].addEventListener('blur', blurListener, true);
   };
});

该指令侦听事件并相应地在范围内设置值,因此我们可以进行条件更改。

这里有几点需要注意。

focusblur 事件没有 "bubble",我们需要使用 "event capturing" 来捕捉它们。这就是为什么不使用 element.on('focus/blur') (它不允许捕获,afaik)而是使用 addEventListener 方法的原因。此方法允许我们通过相应地将第三个参数设置为 falsetrue 来指定侦听器是否将在 "event bubbling" 或 "event capturing" 上执行。

我们本可以使用 focusinfocusout 事件 "bubble",不幸的是 Firefox (focusin and focusout) 不支持这些事件。

这里是 plunker 的实现。

更新: 在我看来,这可以通过使用 :focus pseudo-class 的纯 CSS 来完成,唯一的缺点是占位符需要相对于输入元素处于适当的位置(兄弟) .参见 codepen

DEMO

我创建了一个小指令,可用于您的需要:

app.directive('childFocus', function($window){

  var registered = [];

  // observing focus events in single place
  $window.addEventListener('focus', function(event){
    registered.forEach(function(element){
      if(element.contains(event.target)){
        // if element with focus is a descendant of the 
        // element with our directive then action is triggered
        element._scope.$apply(element._scope.action);
      } 
    });
  }, true)

  return {
    scope : {
      action : '&childFocus' // you can pass whatever expression here
    },
    link : function(scope, element){

      // keep track ref to scope object
      element[0]._scope = scope;

      // (probably better would be to register
      // scope with attached element)
      registered.push(element[0]);

      scope.$on('destroy', function(){
        registered.splice(registered.indexOf(element[0]),1);
      }); 
    }
  }
});

您可以使用 '.parent *' 选择器的 focus 事件来捕获任何焦点事件,然后遍历每个父 DIV 并使用 :focus JQuery 选择器检查具有焦点的子元素,然后将 class 添加到父元素 DIV 并将该 class 用于 show/hide 占位符(see this jsfiddle):

$(function(){
    $('.parent *').focus(function(){
        $('.selected').removeClass('selected');
        $('.parent').each(function(index, el){
            (function($el){
                setTimeout(function(){
                    console.log($el.attr('id'));
                    if($el.find(':focus').length){
                        $el.addClass('selected');
                    }    
                });
            })($(el));
        });
    });
});
.parent{
    padding:1rem;
    margin:1rem;
    border:solid 1px green;
}
.selected{
    border:solid 1px red;
}
.parent .placeholder{
    display:none;
}
.parent.selected .placeholder{
    display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='parent' id='div1'>
    <input type="text" />
    <div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div2'>
    <input type="text" />
    <div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div3'>
    <input type="text" />
    <div class='placeholder'>Placeholder</div>
</div>
<div class='parent' id='div4'>
    <input type="text" />
    <div class='placeholder'>Placeholder</div>
</div>