Phát hiện dấu chấm lửng tràn văn bản HTML


176

Tôi có một bộ sưu tập các yếu tố khối trên một trang. Tất cả chúng đều có các quy tắc CSS không gian trắng, tràn, tràn văn bản để văn bản tràn được cắt bớt và sử dụng dấu chấm lửng.

Tuy nhiên, không phải tất cả các yếu tố tràn.

Có cách nào tôi có thể sử dụng javascript để phát hiện phần tử nào đang tràn không?

Cảm ơn.

Đã thêm: ví dụ cấu trúc HTML tôi đang làm việc.

<td><span>Normal text</span></td>
<td><span>Long text that will be trimmed text</span></td>

Các phần tử SPAN luôn nằm gọn trong các ô, chúng có quy tắc dấu chấm lửng được áp dụng. Tôi muốn phát hiện khi dấu chấm lửng được áp dụng cho nội dung văn bản của SPAN.



10
Không phải là một bản sao! Câu hỏi đó là về một yếu tố trong một yếu tố khác, yếu tố cha. Tôi đang nói về văn bản trong một yếu tố duy nhất. Trong trường hợp của tôi, SPAN trong TD không bao giờ vượt quá TD, đó là văn bản trong SPAN tràn ra và bị cắt xén. Đó là những gì tôi đang cố gắng phát hiện! Xin lỗi - tôi có thể đặt ra câu hỏi này tốt hơn tôi thừa nhận.
Deanoj

Ồ, tôi đã quên thêm - điều này chỉ cần hoạt động trên webkit nếu điều đó giúp ...
deanoj

Tôi đã làm Ghommey chỉ để xem nó có hoạt động không ... không.
Deanoj

khía cạnh dấu chấm lửng là không liên quan; tất cả những gì bạn cần phát hiện là liệu nó có bị tràn không.
Spudley

Câu trả lời:


114

Đã có lúc tôi cần phải làm điều này, và giải pháp đáng tin cậy trên nhiều trình duyệt mà tôi gặp phải là hack job. Tôi không phải là fan hâm mộ lớn nhất của các giải pháp như thế này, nhưng nó chắc chắn tạo ra kết quả chính xác hết lần này đến lần khác.

Ý tưởng là bạn sao chép phần tử, loại bỏ bất kỳ chiều rộng giới hạn nào và kiểm tra xem phần tử nhân bản có rộng hơn bản gốc hay không. Nếu vậy, bạn biết nó sẽ bị cắt ngắn.

Ví dụ: sử dụng jQuery:

var $element = $('#element-to-test');
var $c = $element
           .clone()
           .css({display: 'inline', width: 'auto', visibility: 'hidden'})
           .appendTo('body');

if( $c.width() > $element.width() ) {
    // text was truncated. 
    // do what you need to do
}

$c.remove();

Tôi đã tạo một jsFiddle để chứng minh điều này, http://jsfiddle.net/cgzW8/2/

Bạn thậm chí có thể tạo bộ chọn giả tùy chỉnh của riêng mình cho jQuery:

$.expr[':'].truncated = function(obj) {
  var $this = $(obj);
  var $c = $this
             .clone()
             .css({display: 'inline', width: 'auto', visibility: 'hidden'})
             .appendTo('body');

  var c_width = $c.width();
  $c.remove();

  if ( c_width > $this.width() )
    return true;
  else
    return false;
};

Sau đó sử dụng nó để tìm các yếu tố

$truncated_elements = $('.my-selector:truncated');

Bản trình diễn: http://jsfiddle.net/cgzW8/293/

Hy vọng điều này sẽ giúp, hacky như nó là.


1
@Lauri Không; Cắt bớt CSS không thay đổi văn bản thực tế trong hộp, vì vậy nội dung luôn giống nhau, cho dù đó là cắt ngắn, hiển thị hoặc ẩn. Vẫn không có cách nào để lập trình văn bản bị cắt bớt, nếu có thì bạn sẽ không cần sao chép phần tử ở vị trí đầu tiên!
Christian

1
Có vẻ như điều này sẽ không hoạt động trong tình huống không có white-space: nowrap.
Jakub Hampl

2
Tôi phải nói rằng sau khi tìm kiếm RẤT NHIỀU trên internet và cố gắng thực hiện nhiều giải pháp, đây là giải pháp đáng tin cậy nhất mà tôi tìm thấy. Giải pháp này không cho kết quả khác nhau giữa các trình duyệt như Element.innerWidth hoặc Element. OffersetWidth có vấn đề khi sử dụng lề hoặc phần đệm .. Giải pháp tuyệt vời, Hoàn thành tốt.
Kinh thánh

1
Đối với tôi, điều này đã không làm việc ở bất cứ đâu. Tôi không chắc chắn, nó phụ thuộc vào CSS như thế nào (tôi đã thử sử dụng nó trong các điều khiển đầu vào, các giải pháp bên dưới hoạt động ít nhất trong Chrome và Firefox (nhưng không phải trong IE11)) ...
Alexander

3
Giải pháp tuyệt vời, đặc biệt là sử dụng bộ chọn giả jQuery. Nhưng đôi khi có thể không hoạt động, vì chiều rộng được tính không chính xác nếu văn bản phần tử có kiểu khác (cỡ chữ, khoảng cách chữ, lề của các phần tử bên trong). Trong trường hợp đó, tôi khuyên bạn nên nối phần tử nhân bản vào $ this.parent () thay vì 'body'. Điều này sẽ cung cấp bản sao chính xác của các yếu tố và tính toán sẽ chính xác. Dù sao cũng cảm ơn
Alex

286

Hãy thử hàm JS này, chuyển phần tử span làm đối số:

function isEllipsisActive(e) {
     return (e.offsetWidth < e.scrollWidth);
}

10
Đây phải là câu trả lời - rất thanh lịch và hoạt động trên Chrome và IE (9)
Roy Truelove

14
Câu trả lời này và câu trả lời của Alex sẽ không hoạt động trong IE8; có một số trường hợp kỳ lạ trong đó chiều rộng cuộn và độ rộng ngoài là như nhau ... nhưng nó có dấu chấm lửng, và sau đó một số trường hợp giống nhau ... và KHÔNG có dấu chấm lửng. Nói cách khác, giải pháp đầu tiên là giải pháp duy nhất hoạt động trên tất cả các trình duyệt.

3
Đối với những người cần hiểu offsetWidth, scrollWidth và clientWidth, đây là một lời giải thích rất hay: stackoverflow.com/a/21064102/1815779
Linh Dam

4
Trên text-overflow:ellipsisđó nên làe.offsetWidth <= e.scrollWidth
oriadam

4
Chúng luôn bình đẳng với nhau trên những đứa trẻ linh hoạt, và do đó, sẽ không hiệu quả với chúng.
Kevin Beal

14

Thêm vào câu trả lời của italo, bạn cũng có thể thực hiện việc này bằng jQuery.

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.width() < $jQueryObject[0].scrollWidth);
}

Ngoài ra, như Smoky đã chỉ ra, bạn có thể muốn sử dụng jQuery outsWidth () thay vì width ().

function isEllipsisActive($jQueryObject) {
    return ($jQueryObject.outerWidth() < $jQueryObject[0].scrollWidth);
}

9

Đối với những người sử dụng (hoặc dự định sử dụng) câu trả lời được chấp nhận từ Christian Varga, vui lòng lưu ý các vấn đề về hiệu suất.

Nhân bản / thao tác DOM theo cách như vậy gây ra DOM Reflow (xem phần giải thích về trào ngược DOM ở đây) cực kỳ tốn tài nguyên.

Việc sử dụng giải pháp của Christian Varga trên hơn 100 thành phần trên một trang đã gây ra độ trễ phản xạ 4 giây trong đó luồng JS bị khóa. Xem xét JS là một luồng đơn, điều này có nghĩa là độ trễ UX đáng kể cho người dùng cuối.

Câu trả lời của Italo Borssatto phải là câu trả lời được chấp nhận, nó nhanh hơn khoảng 10 lần trong quá trình lập hồ sơ của tôi.


2
Thật không may, nó không hoạt động trong mọi kịch bản (tôi ước nó sẽ như vậy). Và hiệu năng mà bạn đề cập ở đây có thể được bù lại bằng cách chỉ chạy nó trong tình huống mà bạn cần kiểm tra - giống như chỉ mouseenterđể xem liệu có cần hiển thị một chú giải công cụ hay không.
Jacob

5

Trả lời từ italo là rất tốt! Tuy nhiên, hãy để tôi tinh chỉnh nó một chút:

function isEllipsisActive(e) {
   var tolerance = 2; // In px. Depends on the font you are using
   return e.offsetWidth + tolerance < e.scrollWidth;
}

Tương thích trình duyệt chéo

Trên thực tế, nếu bạn thử mã ở trên và sử dụng console.logđể in ra các giá trị của e.offsetWidthe.scrollWidth, bạn sẽ nhận thấy, trên IE, ngay cả khi bạn không cắt ngắn văn bản, có sự khác biệt về giá trị 1pxhoặc 2pxcó kinh nghiệm.

Vì vậy, tùy thuộc vào kích thước phông chữ bạn sử dụng, cho phép một dung sai nhất định!


Nó không hoạt động trên IE10 - offsetWidth giống hệt với scrollWidth, cả hai đều cho tôi chiều rộng bị cắt ngắn.
SsjCosty

1
Tôi cũng cần dung sai này trên Chrome 60 (mới nhất).
Jan Żankowski

5

elem.offsetWdith VS ele.scrollWidth Công việc này đối với tôi! https://jsfiddle.net/gustavojuan/210to9p1/

$(function() {
  $('.endtext').each(function(index, elem) {
    debugger;
    if(elem.offsetWidth !== elem.scrollWidth){
      $(this).css({color: '#FF0000'})
    }
  });
});

Cả Chrome và Firefox hiện tại đều không hoạt động. Cả hai yếu tố trở thành màu đỏ.
xehpuk

4

Mẫu này hiển thị chú giải công cụ trên bảng ô với văn bản bị cắt ngắn. Là động dựa trên chiều rộng bảng:

$.expr[':'].truncated = function (obj) {
    var element = $(obj);

    return (element[0].scrollHeight > (element.innerHeight() + 1)) || (element[0].scrollWidth > (element.innerWidth() + 1));
};

$(document).ready(function () {
    $("td").mouseenter(function () {
        var cella = $(this);
        var isTruncated = cella.filter(":truncated").length > 0;
        if (isTruncated) 
            cella.attr("title", cella.text());
        else 
            cella.attr("title", null);
    });
});

Bản trình diễn: https://jsfiddle.net/t4qs3tqs/

Nó hoạt động trên tất cả các phiên bản của jQuery


1

Tôi nghĩ rằng cách tốt hơn để phát hiện nó đang sử dụng getClientRects(), có vẻ như mỗi trực tràng có cùng chiều cao, vì vậy chúng ta có thể tính toán số dòng với số lượng topgiá trị khác nhau .

getClientRects Làm việc như này

function getRowRects(element) {
    var rects = [],
        clientRects = element.getClientRects(),
        len = clientRects.length,
        clientRect, top, rectsLen, rect, i;

    for(i=0; i<len; i++) {
        has = false;
        rectsLen = rects.length;
        clientRect = clientRects[i];
        top = clientRect.top;
        while(rectsLen--) {
            rect = rects[rectsLen];
            if (rect.top == top) {
                has = true;
                break;
            }
        }
        if(has) {
            rect.right = rect.right > clientRect.right ? rect.right : clientRect.right;
            rect.width = rect.right - rect.left;
        }
        else {
            rects.push({
                top: clientRect.top,
                right: clientRect.right,
                bottom: clientRect.bottom,
                left: clientRect.left,
                width: clientRect.width,
                height: clientRect.height
            });
        }
    }
    return rects;
}

getRowRectslàm việc như thế này

bạn có thể phát hiện như thế này


1

Tất cả các giải pháp không thực sự hiệu quả với tôi, những gì đã làm là so sánh các yếu tố scrollWidthvớiscrollWidth cha mẹ của nó (hoặc con, tùy thuộc vào yếu tố nào có kích hoạt).

Khi đứa trẻ scrollWidthcao hơn cha mẹ, điều đó có nghĩa .text-ellipsislà hoạt động.


Khi nào eventlà phần tử cha

function isEllipsisActive(event) {
    let el          = event.currentTarget;
    let width       = el.offsetWidth;
    let widthChild  = el.firstChild.offsetWidth;
    return (widthChild >= width);
}

Khi nào eventlà phần tử con

function isEllipsisActive(event) {
    let el          = event.currentTarget;
    let width       = el.offsetWidth;
    let widthParent = el.parentElement.scrollWidth;
    return (width >= widthParent);
}

0

Các e.offsetWidth < e.scrollWidthgiải pháp không phải luôn luôn làm việc.

Và nếu bạn muốn sử dụng JavaScript thuần túy, tôi khuyên bạn nên sử dụng điều này:

(bản đánh máy)

public isEllipsisActive(element: HTMLElement): boolean {
    element.style.overflow = 'initial';
    const noEllipsisWidth = element.offsetWidth;
    element.style.overflow = 'hidden';
    const ellipsisWidth = element.offsetWidth;

    if (ellipsisWidth < noEllipsisWidth) {
      return true;
    } else {
      return false;
    }
}

0

Giải pháp @ItaloBorssatto là hoàn hảo. Nhưng trước khi nhìn vào SO - tôi đã đưa ra quyết định của mình. Đây rồi :)

const elems = document.querySelectorAll('span');
elems.forEach(elem => {
  checkEllipsis(elem);
});

function checkEllipsis(elem){
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const styles = getComputedStyle(elem);
  ctx.font = `${styles.fontWeight} ${styles.fontSize} ${styles.fontFamily}`;
  const widthTxt = ctx.measureText(elem.innerText).width;
  if (widthTxt > parseFloat(styles.width)){
    elem.style.color = 'red'
  }
}
span.cat {
    display: block;
    border: 1px solid black;
    white-space: nowrap;
    width: 100px;
    overflow: hidden;
    text-overflow: ellipsis;
}
 <span class="cat">Small Cat</span>
      <span class="cat">Looooooooooooooong Cat</span>


0

Tôi thực hiện)

const items = Array.from(document.querySelectorAll('.item'));
items.forEach(item =>{
    item.style.color = checkEllipsis(item) ? 'red': 'black'
})

function checkEllipsis(el){
  const styles = getComputedStyle(el);
  const widthEl = parseFloat(styles.width);
  const ctx = document.createElement('canvas').getContext('2d');
  ctx.font = `${styles.fontSize} ${styles.fontFamily}`;
  const text = ctx.measureText(el.innerText);
  return text.width > widthEl;
}
.item{
  width: 60px;
  overflow: hidden;
  text-overflow: ellipsis;
}
      <div class="item">Short</div>
      <div class="item">Loooooooooooong</div>


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.