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>