1

I'm having overflowed text and I want to display the number of words that are hidden by overflow.

For example: I'm having the string: I want to see the overflowed words that are hidden in here. And my div can only contain 3 words. So I want to see: I want to +9.

This is what I have:

let items = [
  "item 1 - class 1;",
  "item 2 - class 1;",
  "item 3 - class 1;",
  "item 4 - class 1;",
  "item 5 - class 1;",
  "item 6 - class 1;",
  "item 7 - class 1;",
  "item 8 - class 1;",
];

let app = angular.module('app', []);
app.directive('my', function() {
  return {
    controller: function($scope) {
      $scope.items = items;
    }
  }
});
.a {
  white-space: nowrap;
  overflow: hidden;
  width: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<body ng-app="app">
  <div my class="a">
    <span ng-repeat="item in items">{{item}}</span>
  </div>
</body>

I can't find a way to do 2 things: 1. show only fully items and not partly items on the row. 2. show a text that say (+8) for example, that can expend and see overflowed 8 items.

Omri Attiya
  • 3,917
  • 3
  • 19
  • 35

3 Answers3

2

UPDATE after comment:

Okay, after the clarification I think I have a better handle on what you are trying to achieve. The code below almost exactly does what you want - but this is not an elegant solution, for sure. The fixed width, the character-limit still being there as a constant are both liabilities (e.g. when it comes to responsive design, smaller/larger font sizes, etc.). Also the code I did is far from a semantic html or a proper JS/AngularJS but I hope it will guide you in some direction.

Two important bits of the code:

1) the ::after content, with dynamic attribute which is also clickable. Fiddle around with this concept, I believe this will help you a lot figure out the optimal solution

2) the css with two visible states (open/closed) and targeting it specifically by a dynamic ID

I hope this helps, let me know if something is unclear or if this is still not what you are on to achieve.


This is a bit of a workaround and I am not even sure if this is what you tried to achieve regarding the overflow effect. Please note that performance-wise this slows down at large entries (though I believe you are not expecting to display thousands of lines like this). Obviously this is not a full solution but might direct you towards a possible way. Let me know in comments if this is absolutely not something you were hoping for - then we can figure out a way.

let items = [
  "This is a very long text to cut",
  "This is short",
  "Buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuut whaaaaaaaaaaaaaaaaaaaat is this?",
  "This is super lengthy. This is super lengthy. This is super lengthy. This is super lengthy. This is super lengthy. This is super lengthy. This is super lengthy. This is super lengthy. "
];

let app = angular.module('app', []);
app.directive('my', function() {
  return {
    controller: function($scope, $timeout) {
      $scope.items = [];

      let limitChar = 20; // set it according to width, though the css of element p will do the rest of the magic
      $.each(items,function(index,item){
        let words = item.trim().split(" ");  // trim is required not to add empty strings to the array when visibleWords is created below

        let visible = item.substring(0, limitChar).trim();
        let visibleWords = visible.split(" ");
        let remainingWordCount = words.length - visibleWords.length;

        let entry = {
          "open" : false,
          "trimmed" : visibleWords.join(" "),
          "count" : remainingWordCount,
          "full" : item
        }

        $scope.items.push(entry)
      })

      $scope.opener = function(item, id){
        item.open = true;
        // the timeout is required so that the DOM refreshes first and the item_id_open dom element is actually existing.
        $timeout(function(){
          $('#' + 'item_' + id + "_open").css({
              'overflow' : 'initial',
              'text-overflow' : 'initial',
              'white-space' : 'normal'
          });
        },0)
      }

      $scope.closer = function(item,id){
        item.open = false;
        // the timeout is required so that the DOM refreshes first and the item_id_open dom element is actually existing.
        $timeout(function(){
          $('#' + 'item_' + id + "_closed").css({
              'overflow' : 'hidden',
              'text-overflow' : 'ellipsis',
              'white-space' : 'nowrap'
          });
        },0)
      }
    }
  }
});
.container2{
  position:relative;
  float:left;
  background-color:green;
  width:180px;
}

.element{
  width:180px;
  position:relative;
  float:left;
}

.element p{
  position:relative;
  float:left;
  display:inline-block;
  width:150px;
  margin:10px 0;
  overflow:hidden;
  text-overflow:ellipsis; /* this is optional, can also be substituted with modifying the width */
  padding-right:5px;
  word-break:break-word;
  white-space:nowrap;
}

.element span{
  position:absolute;
  right:0px;
  top:10px;
  display:inline-block;
}

.element span::after{
  content: "(+" attr(remainder) ")";
  float:left;
  position:relative;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>

<body ng-app="app">
  <div my>
    <div class="container2">
      <div flex="100" class="element" ng-repeat="item in items track by $index">
        <p ng-if="!item.open" id="{{'item_' + $index + '_closed'}}">
          {{item.trimmed}}
        </p>
        <span ng-if="!item.open && item.count > 0" ng-click="opener(item, $index)" style="cursor:pointer" remainder="{{item.count}}"></span>

        <p ng-if="item.open" id="{{'item_' + $index + '_open'}}">
          {{item.full}}
        </p>
        <span ng-if="item.open" ng-click="closer(item, $index)" style="cursor:pointer" remainder="{{item.count}}"></span>
      </div>
    </div>
  </div>
</body>
Andrew Adam
  • 1,572
  • 9
  • 22
  • the point is that my `limitNum` is changing all the time. Because that I'm not dealing with words but with sentences, and each sentence is of different length it creates a problem. For example, at your code, if one of the first 3 words were something like `LIFEEEEEEEEEEEEEEEEEEE` you'll see that it will overflow and it won't work as expected. – Omri Attiya Jan 21 '20 at 18:34
  • @OmriAttiya I have edited the answer with a newer, more accurate solution. I hope this helps at least to show you some new alternatives. – Andrew Adam Jan 22 '20 at 00:12
  • @OmriAttiya would you mind accepting it as an answer or do you need any further clarification/modification? – Andrew Adam Jan 27 '20 at 08:53
0

You can create a variable that hold the length of the hole text minus the length of the text that can view in your case "I want to" is 9 chars then

$scope.hiddenChars=YourHoleText.length-9;

and display $scope.hiddenChars value in case it is greater than zero

Ibrahem Uwk
  • 137
  • 9
0

I think you can do it by using el.offsetLeft - el.scrollLeft and then getBoundingClientRect() to get the width and see if the element is inside the <div my class="a">:

https://codepen.io/Alexander9111/pen/OJPaNBP

So far I have only looped through to see which elements (spans) are inside, half-inside and outside the div.a - I am not sure how to handle it from here?

(Note the first function getOffset comes from: https://stackoverflow.com/a/442474/9792594 and then the code below is mine.

//getOffset take from:
//https://stackoverflow.com/a/442474/9792594
function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

document.addEventListener('DOMContentLoaded',
 function(){
  const spans = document.querySelectorAll("span");
  const a = document.querySelector(".a");
  const a_rect = a.getBoundingClientRect();
  const box = getOffset(a);
  for (i = 0; i < spans.length; i++){
    const el = getOffset( spans[i]);
    rect = spans[i].getBoundingClientRect();
    console.log(i, el.left, el.left + rect.width);
    if ((el.left + rect.width) < (box.left + a_rect.width)){
      console.log(i, "inside")
    } else if (el.left < (box.left + a_rect.width)) {
      console.log(i, "half inside")         
    } else {
      console.log(i, "outside")
    }
  }

}, false);
Alex L
  • 4,168
  • 1
  • 9
  • 24