ng-mouseover and leave to toggle item using mouse in angularjs
Asked Answered
U

5

65

HTML:

<ul ng-repeat="task in tasks">
    <li ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()">{{task.name}}</li>
    <span ng-show="hoverEdit"><a>Edit</a></span>
</ul>

JS:

$scope.hoverIn = function(){
    $scope.hoverEdit = true;
};

$scope.hoverOut = function(){
    $scope.hoverEdit = false;
};

The code is ridiculous because I think it's too much. I think it can be simplified. Anyway the result toggle all the item once it's hovered. I've jQuery background, so I have no idea how to make single item work in ng-repeat.

Univocal answered 20/3, 2014 at 12:12 Comment(1)
In my opinion, too much obfuscation; angular will define your variable on the fly if you just write <ng-mouseover="hoverEdit=true;"> and <ng-mouseleave="hoverEdit=false;"> --- for clickable toggles i use ng-click="flag=!flag" which just flips it from undefined to true, to false, to true, etc. and undefined is "falsey" so !!flag would be "false" and !flag is "true" even when typeof flag === 'undefined'Rescind
H
94

Angular solution

You can fix it like this:

$scope.hoverIn = function(){
    this.hoverEdit = true;
};

$scope.hoverOut = function(){
    this.hoverEdit = false;
};

Inside of ngMouseover (and similar) functions context is a current item scope, so this refers to the current child scope.

Also you need to put ngRepeat on li:

<ul>
    <li ng-repeat="task in tasks" ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()">
        {{task.name}}
        <span ng-show="hoverEdit">
            <a>Edit</a>
        </span>
    </li>
</ul>

Demo

CSS solution

However, when possible try to do such things with CSS only, this would be the optimal solution and no JS required:

ul li span {display: none;}
ul li:hover span {display: inline;}
Hamilton answered 20/3, 2014 at 12:20 Comment(4)
that's cool. but I'm now more confused on $scope usage haha. one question, how to animate the appearing item? using css transition possible?Univocal
Why are you confused? ngRepeat creates separate child scrope for each item. But $scope is a parent scope of all this child scopes.Hamilton
oh I see. thanks for explanation. maybe I should study back this in pure js versus $(this) in jquery.Univocal
CSS solution is the cleanest and the most elegant.Kidnap
M
19

I would simply make the assignment happen in the ng-mouseover and ng-mouseleave; no need to bother js file :)

<ul ng-repeat="task in tasks">
    <li ng-mouseover="hoverEdit = true" ng-mouseleave="hoverEdit = false">{{task.name}}</li>
    <span ng-show="hoverEdit"><a>Edit</a></span>
</ul>
Maus answered 18/11, 2014 at 0:44 Comment(1)
I was wondering how to add an active class with hover in Angular. This simple little block along with ng-class="{'active': hover}" did the trick!Melissiamelita
A
12

I'd probably change your example to look like this:

<ul ng-repeat="task in tasks">
  <li ng-mouseover="enableEdit(task)" ng-mouseleave="disableEdit(task)">{{task.name}}</li>
  <span ng-show="task.editable"><a>Edit</a></span>
</ul>

//js
$scope.enableEdit = function(item){
  item.editable = true;
};

$scope.disableEdit = function(item){
  item.editable = false;
};

I know it's a subtle difference, but makes the domain a little less bound to UI actions. Mentally it makes it easier to think about an item being editable rather than having been moused over.

Example jsFiddle.

Artiste answered 20/3, 2014 at 12:23 Comment(2)
which one is better? compare to dfsq's answer? I found this is more confusing lolUnivocal
@Univocal - I wouldn't say either one is better/worse than the other. I tend to avoid using the this pointer inside methods like these because it's not obvious what this is referring to. In dfsq's answer, it happens to refer to the the $scope of the individual repeater items. However, if called outside the repeater it would refer to the parent $scopeArtiste
P
6

A little late here, but I've found this to be a common problem worth a custom directive to handle. Here's how that might look:

  .directive('toggleOnHover', function(){
    return {
      restrict: 'A',
      link: link
    };

    function link(scope, elem, attrs){
      elem.on('mouseenter', applyToggleExp);
      elem.on('mouseleave', applyToggleExp);

      function applyToggleExp(){
        scope.$apply(attrs.toggleOnHover);
      }
    }

  });

You can use it like this:

<li toggle-on-hover="editableProp = !editableProp">edit</li> 
Phlegmy answered 21/10, 2015 at 19:49 Comment(1)
Very good solution, works great when you add your own scope function, such as: scope.setHoverState = function(row, hoverType) {// code here} and in your view toggle-on-hover="setHoverState(item, 'myType')"Shrewsbury
L
0

Here is example with only CSS for that. In example I'm using SASS and SLIM.

https://codepen.io/Darex1991/pen/zBxPxe

Slim:

a.btn.btn--joined-state
  span joined
  span leave

SASS:

=animate($property...)
  @each $vendor in ('-webkit-', '')
    #{$vendor}transition-property: $property
    #{$vendor}transition-duration: .3s
    #{$vendor}transition-timing-function: ease-in

=visible
  +animate(opacity, max-height, visibility)
  max-height: 150px
  opacity: 1
  visibility: visible

=invisible
  +animate(opacity, max-height, visibility)
  max-height: 0
  opacity: 0
  visibility: hidden

=transform($var)
  @each $vendor in ('-webkit-', '-ms-', '')
    #{$vendor}transform: $var

.btn
  border: 1px solid blue

  &--joined-state
    position: relative
    span
      +animate(opacity)

    span:last-of-type
      +invisible
      +transform(translateX(-50%))
      position: absolute
      left: 50%

    &:hover
      span:first-of-type
        +invisible
      span:last-of-type
        +visible
        border-color: blue
Limiting answered 1/6, 2016 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.