Bagaimana cara menghitung baris teks di dalam elemen DOM? Bisakah saya?

127

Saya bertanya-tanya apakah ada cara untuk menghitung baris di dalam div misalnya. Katakanlah kita memiliki div seperti ini:

<div id="content">hello how are you?</div>

Bergantung pada banyak faktor, div dapat memiliki satu, atau dua, atau bahkan empat baris teks. Apakah ada cara untuk mengetahui skrip?

Dengan kata lain, apakah jeda otomatis terwakili di DOM?

buti-oxa
sumber

Jawaban:

89

Jika ukuran div bergantung pada konten (yang saya anggap kasus dari deskripsi Anda) maka Anda dapat mengambil tinggi div menggunakan:

var divHeight = document.getElementById('content').offsetHeight;

Dan bagi dengan tinggi garis font:

document.getElementById('content').style.lineHeight;

Atau untuk mendapatkan tinggi baris jika belum diatur secara eksplisit:

var element = document.getElementById('content');
document.defaultView.getComputedStyle(element, null).getPropertyValue("lineHeight");

Anda juga perlu mempertimbangkan padding dan antar-baris.

EDIT

Tes mandiri sepenuhnya, secara eksplisit menyetel tinggi baris:

function countLines() {
   var el = document.getElementById('content');
   var divHeight = el.offsetHeight
   var lineHeight = parseInt(el.style.lineHeight);
   var lines = divHeight / lineHeight;
   alert("Lines: " + lines);
}
<body onload="countLines();">
  <div id="content" style="width: 80px; line-height: 20px">
    hello how are you? hello how are you? hello how are you? hello how are you?
  </div>
</body>

Kolam
sumber
7
Ini adalah permulaan yang baik kecuali bahwa ketinggian garis mungkin tidak selalu disetel. Anda harus mendapatkannya dari gaya elemen yang dihitung.
Chetan S
Jika dipikir-pikir, tinggi garis tidak harus selalu numerik (atau dalam piksel). Jadi, jika kode Anda bergantung padanya dan konvensi CSS Anda mengizinkannya, Anda mungkin harus menyetel tinggi baris dalam piksel.
Chetan S
6
@Chetan - mengatur dimensi teks dalam piksel umumnya dianggap hal yang buruk. astahost.com/Sizes-Webdesign-Em-Vs-Px-t8926.html
annakata
6
Ini hanya dapat berfungsi dalam kasus yang paling sederhana (seperti contoh saya). Jika ada bentang di dalam, elemen blok-sebaris dan seterusnya, pembagian langsung dengan ukuran font (induk) tidak ada gunanya. Tetap saja, itu lebih baik daripada tidak sama sekali, terima kasih.
buti-oxa
2
Saya pikir metode yang lebih baik untuk mendapatkan komputasi line-height(yang tidak diatur secara eksplisit) adalah dengan menggunakanwindow.getComputedStyle(element, null).getPropertyValue('line-height')
rnevius
20

Salah satu solusinya adalah memasukkan setiap kata dalam tag span menggunakan script. Kemudian jika dimensi Y dari tag span tertentu lebih kecil daripada dimensi Y dari tag span yang diberikan sebelumnya, maka baris baru telah terjadi.

Bob Brunius
sumber
Pintar! Bagaimana kamu melakukannya? Saya kira Anda menganggap teks hanya paragraf (seperti yang saya miliki dalam contoh). Saya tidak memiliki batasan ini dalam pikiran, tetapi mungkin OK, asalkan skrip tidak macet ketika ada hal lain di dalam paragraf.
buti-oxa
1
Setelah dipikir-pikir, metode ini gagal jika ada bentang yang sejajar secara vertikal dalam garis. Jadi, bahkan paragraf teks saja dapat dihitung dengan salah.
buti-oxa
Div saya memiliki teks dan gambar ... untungnya gambarnya benar-benar diposisikan jadi saya pikir mereka akan dikecualikan. : D Bagaimanapun saya menggunakan kode yang sama yang Anda sarankan tetapi dengan div bukan rentang, terima kasih +1!
Albert Renshaw
20

Lihat fungsi getClientRects () yang bisa digunakan untuk menghitung jumlah baris dalam sebuah elemen. Berikut ini contoh cara menggunakannya.

var message_lines = $("#message_container")[0].getClientRects();

Ini mengembalikan objek DOM javascript. Jumlah garis dapat diketahui dengan melakukan ini:

var amount_of_lines = message_lines.length;

Itu dapat mengembalikan ketinggian setiap baris, dan banyak lagi. Lihat berbagai hal yang dapat dilakukan dengan menambahkan ini ke skrip Anda, lalu cari di log konsol Anda.

console.log("");
console.log("message_lines");
console.log(".............................................");
console.dir(message_lines);
console.log("");

Meskipun beberapa hal yang perlu diperhatikan adalah ini hanya berfungsi jika elemen penampungnya sebaris, namun Anda dapat mengelilingi elemen inline penampung dengan elemen blok untuk mengontrol lebarnya seperti ini:

<div style="width:300px;" id="block_message_container">
<div style="display:inline;" id="message_container">
..Text of the post..
</div>
</div>

Meskipun saya tidak merekomendasikan pengkodean keras gaya seperti itu. Itu hanya untuk tujuan contoh.

Digital-Christie
sumber
3
Ini dan jawaban berbasis getClientRects lainnya jauh lebih baik daripada yang diterima
matteo
2
Ini harus menjadi jawaban yang diterima. Terima kasih @ Digital-Christie
Antuan
12

Saya tidak puas dengan jawaban di sini dan pertanyaan lain. Jawaban berperingkat tertinggi tidak memperhitungkan paddingatau bordermemperhitungkan, dan karena itu jelas mengabaikan box-sizingjuga. Jawaban saya menggabungkan beberapa teknik di sini dan dan di utas lainnya untuk mendapatkan solusi yang sesuai dengan kepuasan saya.

Ini tidak sempurna: Ketika tidak ada nilai numerik yang dapat diambil untuk line-height(misalnya normalatau inherit), ini hanya menggunakan font-sizedikalikan dengan 1.2. Mungkin orang lain dapat menyarankan cara yang andal untuk mendeteksi nilai piksel dalam kasus tersebut.

Selain itu, ia mampu menangani dengan benar sebagian besar gaya dan kasing yang telah saya lemparkan.

jsFiddle untuk bermain-main dan menguji. Juga sebaris di bawah.

function countLines(target) {
  var style = window.getComputedStyle(target, null);
  var height = parseInt(style.getPropertyValue("height"));
  var font_size = parseInt(style.getPropertyValue("font-size"));
  var line_height = parseInt(style.getPropertyValue("line-height"));
  var box_sizing = style.getPropertyValue("box-sizing");
  
  if(isNaN(line_height)) line_height = font_size * 1.2;
 
  if(box_sizing=='border-box')
  {
    var padding_top = parseInt(style.getPropertyValue("padding-top"));
    var padding_bottom = parseInt(style.getPropertyValue("padding-bottom"));
    var border_top = parseInt(style.getPropertyValue("border-top-width"));
    var border_bottom = parseInt(style.getPropertyValue("border-bottom-width"));
    height = height - padding_top - padding_bottom - border_top - border_bottom
  }
  var lines = Math.ceil(height / line_height);
  alert("Lines: " + lines);
  return lines;
}
countLines(document.getElementById("foo"));
div
{
  padding:100px 0 10% 0;
  background: pink;
  box-sizing: border-box;
  border:30px solid red;
}
<div id="foo">
x<br>
x<br>
x<br>
x<br>
</div>

Jeff
sumber
membantu posting ... Terima kasih
Sinto
Pertimbangan yang bagus, tetapi dalam kasus saya, tinggi satu baris div lebih dari tinggi baris sebesar 1px, tanpa padding dan border. Jadi mendapatkan tinggi garis dengan menyisir jawaban Anda dan jawaban @ e-info128 (metode cloneNode) mungkin merupakan pilihan yang lebih baik
Eric
8

Kloning objek kontainer dan tulis 2 huruf dan hitung tingginya. Ini mengembalikan tinggi sebenarnya dengan semua gaya diterapkan, tinggi garis, dll. Sekarang, hitung tinggi objek / ukuran huruf. Di Jquery, tingginya melebihi padding, margin, dan border, sangat bagus untuk menghitung tinggi sebenarnya dari setiap baris:

other = obj.clone();
other.html('a<br>b').hide().appendTo('body');
size = other.height() / 2;
other.remove();
lines = obj.height() /  size;

Jika Anda menggunakan font langka dengan tinggi yang berbeda untuk setiap huruf, ini tidak berhasil. Tetapi bekerja dengan semua font normal, seperti Arial, mono, komik, Verdana, dll. Uji dengan font Anda.

Contoh:

<div id="content" style="width: 100px">hello how are you? hello how are you? hello how are you?</div>
<script type="text/javascript">
$(document).ready(function(){

  calculate = function(obj){
    other = obj.clone();
    other.html('a<br>b').hide().appendTo('body');
    size = other.height() / 2;
    other.remove();
    return obj.height() /  size;
  }

  n = calculate($('#content'));
  alert(n + ' lines');
});
</script>

Hasil: 6 Lines

Bekerja di semua browser tanpa fungsi langka di luar standar.

Periksa: https://jsfiddle.net/gzceamtr/

e-info128
sumber
Pertama-tama, saya ingin mengucapkan terima kasih banyak, apa yang saya cari. Bisakah Anda menjelaskan setiap baris dari fungsi hitung. Terima kasih banyak sebelumnya. SYA :)
LebCit
1
bagi mereka yang membutuhkan jawaban non-jQuery: clonecloneNode, hidestyle.visibility = "hidden", html('a<br>b')textContent='a\r\nb', appendTo('body')document.documentElement.appendChild, height()getBoundingClientRect().height
Eric
Karena saya mendapatkan kesalahan 1px antara tinggi garis dan tinggi div, saya merekomendasikan jawaban ini. Menggabungkan ini dan jawaban @ Jeff akan menjadi lebih baik
Eric
6

Bagi mereka yang menggunakan jQuery http://jsfiddle.net/EppA2/3/

function getRows(selector) {
    var height = $(selector).height();
    var line_height = $(selector).css('line-height');
    line_height = parseFloat(line_height)
    var rows = height / line_height;
    return Math.round(rows);
}
penkovsky
sumber
jsfiddle.net/EppA2/9 kurang akurat, tetapi menurut ini mungkin lebih baik didukung oleh berbagai browser
penkovsky
8
Ketika jQuery mengembalikan "normal" dari $(selector).css('line-height');, Anda tidak akan mendapatkan angka dari fungsi itu.
bafromca
5

Saya yakin itu tidak mungkin sekarang. Tapi memang begitu.

Implementasi getClientRects dari IE7 melakukan apa yang saya inginkan. Buka halaman ini di IE8, coba muat ulang dengan lebar jendela yang bervariasi, dan lihat bagaimana jumlah baris pada elemen pertama berubah sesuai. Inilah baris kunci javascript dari halaman itu:

var rects = elementList[i].getClientRects();
var p = document.createElement('p');
p.appendChild(document.createTextNode('\'' + elementList[i].tagName + '\' element has ' + rects.length + ' line(s).'));

Sayangnya bagi saya, Firefox selalu mengembalikan satu persegi panjang klien per elemen, dan IE8 melakukan hal yang sama sekarang. (Halaman Martin Honnen berfungsi hari ini karena IE membuatnya dalam tampilan compat IE; tekan F12 di IE8 untuk bermain dengan mode yang berbeda.)

Ini menyedihkan. Sepertinya sekali lagi implementasi spesifikasi Firefox yang literal tetapi tidak berharga menang atas aplikasi Microsoft yang berguna. Atau apakah saya melewatkan situasi di mana getClientRects baru dapat membantu pengembang?

buti-oxa
sumber
2
Seperti yang ditunjukkan oleh dokumentasi element.getClientRects Mozilla , setidaknya untuk elemen sebaris spesifikasi W3C memang menghasilkan persegi untuk setiap baris teks - tidak ideal, tetapi setidaknya sesuatu.
natevw
getClientRects (). panjang adalah cara yang benar. getComputedStyle () dapat mengembalikan nilai seperti "inherit", "normal" dan "auto".
Binyamin
4

berdasarkan jawaban GuyPaddock dari atas, ini sepertinya berhasil untuk saya

function getLinesCount(element) {
  var prevLH = element.style.lineHeight;
  var factor = 1000;
  element.style.lineHeight = factor + 'px';

  var height = element.getBoundingClientRect().height;
  element.style.lineHeight = prevLH;

  return Math.floor(height / factor);
}

trik di sini adalah meningkatkan tinggi baris sedemikian rupa sehingga akan "menelan" perbedaan browser / OS apa pun dalam cara merender font

Memeriksa dengan berbagai gaya dan ukuran font / keluarga yang berbeda hanya hal yang tidak diperhitungkan (karena dalam kasus saya itu tidak masalah), adalah padding - yang dapat dengan mudah ditambahkan ke solusi.

Maor Yosef
sumber
2

Tidak, tidak bisa diandalkan. Ada terlalu banyak variabel yang tidak diketahui

  1. OS apa (DPI berbeda, variasi font, dll ...)?
  2. Apakah mereka memperbesar ukuran font karena mereka secara praktis buta?
  3. Heck, di browser webkit, Anda sebenarnya dapat mengubah ukuran kotak teks sesuai keinginan Anda.

Daftarnya terus berlanjut. Suatu hari nanti saya berharap akan ada metode yang andal untuk menyelesaikan ini dengan JavaScript, tetapi sampai hari itu tiba, Anda kurang beruntung.

Saya benci jawaban seperti ini dan saya berharap seseorang bisa membuktikan bahwa saya salah.

KyleFarris
sumber
3
Jika Anda menghitung baris setelah rendering, semua yang tidak diketahui itu tidak relevan. Poin Anda berlaku, tentu saja, jika Anda mencoba untuk "menebak" berapa banyak baris yang digunakan browser saat merender teks.
simon
Saya akan mengatakan variabel pengguna yang memperbesar dan / atau mengubah ukuran teks dapat diabaikan. Saya belum pernah melihat situs yang desainnya dioptimalkan untuk mengakomodasi penskalaan jendela (setelah halaman dimuat) atau mengubah ukuran teks berdasarkan browser, jadi menurut saya itu bukan faktor penting dalam pertanyaan khusus ini. Namun saya setuju tidak ada solusi yang dapat diandalkan, hanya "perkiraan" berdasarkan piksel seperti yang ditunjukkan orang lain.
Kalko
2

Anda harus bisa membagi panjang ('\ n') dan mendapatkan jeda baris.

update: ini berfungsi di FF / Chrome tetapi tidak di IE.

<html>
<head>
<script src="jquery-1.3.2.min.js"></script>
<script>
    $(document).ready(function() {
        var arr = $("div").text().split('\n');
        for (var i = 0; i < arr.length; i++)
            $("div").after(i + '=' + arr[i] + '<br/>');
    });
</script>
</head>
<body>
<div>One
Two
Three</div>
</body>
</html>
Chad Grant
sumber
1
Cobalah dan beri tahu kami bagaimana kelanjutannya. Saya sangat serius, tidak sedang menyindir. Anda dapat menggunakan kode ini di konsol FireBug di halaman ini juga. var the_text = $ ('. welovestackoverflow p'). text (); var numlines = the_text.split ("\ n"). length; alert (numlines);
KyleFarris
3
Terima kasih atas kejutan ini, saya tidak tahu itu mungkin, tetapi ini bukan yang saya inginkan. Jquery tampaknya menghitung jeda baris di sumbernya, dan saya tertarik dengan jeda baris otomatis dalam hasilnya. Dalam kasus div "One Two Three" Anda, hasilnya harus 1, karena browser meletakkan semua teks ke dalam satu baris.
buti-oxa
Lalu saya salah memahami pertanyaan Anda. Anda ingin mencari tinggi baris yang dihitung.
Chad Grant
1

getClientRectskembalikan klien rects seperti ini dan jika Anda ingin mendapatkan garis, gunakan fungsi ikuti seperti ini

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;
}
Defims
sumber
1

Saya menemukan cara untuk menghitung nomor baris saat saya mengembangkan editor html. Metode utamanya adalah:

  1. Di IE Anda dapat memanggil getBoundingClientRects, yang mengembalikan setiap baris sebagai persegi panjang

  2. Di webkit atau mesin html standar baru, ia mengembalikan setiap elemen atau persegi panjang klien node, dalam hal ini Anda dapat membandingkan setiap persegi panjang, maksud saya setiap persegi panjang harus ada yang terbesar, jadi Anda dapat mengabaikan persegi panjang yang tingginya lebih kecil (jika ada bagian atas persegi panjang yang lebih kecil dari itu dan bagian bawah lebih besar darinya, kondisinya benar.)

jadi mari kita lihat hasil tesnya:

masukkan deskripsi gambar di sini

Persegi panjang hijau adalah persegi panjang terbesar di setiap baris

Persegi panjang merah adalah batas seleksi

Persegi panjang biru adalah batas dari awal hingga seleksi setelah meluas, kita melihatnya mungkin lebih besar dari persegi panjang merah, jadi kita harus memeriksa setiap bagian bawah persegi panjang untuk membatasinya harus lebih kecil dari bagian bawah persegi panjang merah.

        var lineCount = "?";
        var rects;
        if (window.getSelection) {
            //Get all client rectangles from body start to selection, count those rectangles that has the max bottom and min top
            var bounding = {};
            var range = window.getSelection().getRangeAt(0);//As this is the demo code, I dont check the range count
            bounding = range.getBoundingClientRect();//!!!GET BOUNDING BEFORE SET START!!!

            //Get bounding and fix it , when the cursor is in the last character of lineCount, it may expand to the next lineCount.
            var boundingTop = bounding.top;
            var boundingBottom = bounding.bottom;
            var node = range.startContainer;
            if (node.nodeType !== 1) {
                node = node.parentNode;
            }
            var style = window.getComputedStyle(node);
            var lineHeight = parseInt(style.lineHeight);
            if (!isNaN(lineHeight)) {
                boundingBottom = boundingTop + lineHeight;
            }
            else {
                var fontSize = parseInt(style.fontSize);
                if (!isNaN(fontSize)) {
                    boundingBottom = boundingTop + fontSize;
                }
            }
            range = range.cloneRange();

            //Now we have enougn datas to compare

            range.setStart(body, 0);
            rects = range.getClientRects();
            lineCount = 0;
            var flags = {};//Mark a flags to avoid of check some repeat lines again
            for (var i = 0; i < rects.length; i++) {
                var rect = rects[i];
                if (rect.width === 0 && rect.height === 0) {//Ignore zero rectangles
                    continue;
                }
                if (rect.bottom > boundingBottom) {//Check if current rectangle out of the real bounding of selection
                    break;
                }
                var top = rect.top;
                var bottom = rect.bottom;
                if (flags[top]) {
                    continue;
                }
                flags[top] = 1;

                //Check if there is no rectangle contains this rectangle in vertical direction.
                var succ = true;
                for (var j = 0; j < rects.length; j++) {
                    var rect2 = rects[j];
                    if (j !== i && rect2.top < top && rect2.bottom > bottom) {
                        succ = false;
                        break;
                    }
                }
                //If succ, add lineCount 1
                if (succ) {
                    lineCount++;
                }
            }
        }
        else if (editor.document.selection) {//IN IE8 getClientRects returns each single lineCount as a rectangle
            var range = body.createTextRange();
            range.setEndPoint("EndToEnd", range);
            rects = range.getClientRects();
            lineCount = rects.length;
        }
        //Now we get lineCount here
dexiang.dll
sumber
1

Coba solusi ini:

function calculateLineCount(element) {
  var lineHeightBefore = element.css("line-height"),
      boxSizing        = element.css("box-sizing"),
      height,
      lineCount;

  // Force the line height to a known value
  element.css("line-height", "1px");

  // Take a snapshot of the height
  height = parseFloat(element.css("height"));

  // Reset the line height
  element.css("line-height", lineHeightBefore);

  if (boxSizing == "border-box") {
    // With "border-box", padding cuts into the content, so we have to subtract
    // it out
    var paddingTop    = parseFloat(element.css("padding-top")),
        paddingBottom = parseFloat(element.css("padding-bottom"));

    height -= (paddingTop + paddingBottom);
  }

  // The height is the line count
  lineCount = height;

  return lineCount;
}

Anda dapat melihatnya beraksi di sini: https://jsfiddle.net/u0r6avnt/

Coba ubah ukuran panel pada halaman (untuk membuat sisi kanan halaman lebih lebar atau lebih pendek) dan kemudian jalankan lagi untuk melihat bahwa panel dapat mengetahui berapa banyak garis yang ada.

Masalah ini lebih sulit daripada yang terlihat, tetapi sebagian besar kesulitan berasal dari dua sumber:

  1. Penyajian teks terlalu rendah di browser untuk ditanyai langsung dari JavaScript. Bahkan pseudo-selector CSS :: first-line tidak berperilaku seperti penyeleksi lain (Anda tidak dapat membalikkannya, misalnya, untuk menerapkan gaya ke semua kecuali baris pertama).

  2. Konteks memainkan peran besar dalam cara Anda menghitung jumlah garis. Misalnya, jika tinggi baris tidak secara eksplisit disetel dalam hierarki elemen target, Anda mungkin mendapatkan kembali "normal" sebagai tinggi baris. Selain itu, elemen tersebut mungkin menggunakan box-sizing: border-boxdan oleh karena itu tunduk pada bantalan.

Pendekatan saya meminimalkan # 2 dengan mengontrol ketinggian garis secara langsung dan memfaktorkan dalam metode ukuran kotak, yang mengarah ke hasil yang lebih deterministik.

GuyPaddock
sumber
1

Dalam kasus tertentu, seperti tautan yang membentang di beberapa baris dalam teks tidak rata, Anda bisa mendapatkan jumlah baris dan setiap koordinat dari setiap baris, saat Anda menggunakan ini:

var rectCollection = object.getClientRects();

https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects

Ini berfungsi karena setiap baris akan berbeda meskipun sedikit. Selama ada, mereka digambar sebagai "persegi panjang" yang berbeda oleh perender.

Firsh - LetsWP.io
sumber
0

Mengikuti saran @BobBrunius 2010, saya membuat ini dengan jQuery. Tidak diragukan lagi ini dapat ditingkatkan tetapi mungkin membantu beberapa.

$(document).ready(function() {

  alert("Number of lines: " + getTextLinesNum($("#textbox")));

});

function getTextLinesNum($element) {

  var originalHtml = $element.html();
  var words = originalHtml.split(" ");
  var linePositions = [];
        
  // Wrap words in spans
  for (var i in words) {
    words[i] = "<span>" + words[i] + "</span>";
  }
        
  // Temporarily replace element content with spans. Layout should be identical.
  $element.html(words.join(" "));
        
  // Iterate through words and collect positions of text lines
  $element.children("span").each(function () {
    var lp = $(this).position().top;
    if (linePositions.indexOf(lp) == -1) linePositions.push(lp);
  });
        
  // Revert to original html content
  $element.html(originalHtml);
        
  // Return number of text lines
  return linePositions.length;

}
#textbox {
  width: 200px;
  text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="textbox">Lorem ipsum dolor sit amet, consectetuer adipiscing elit,
  <br>sed diam nonummy</div>

josef
sumber
0

Anda dapat membandingkan tinggi elemen dan tinggi elemen dengan line-height: 0

function lineCount(elm) {
  const style = elm.getAttribute('style')
  elm.style.marginTop = 0
  elm.style.marginBottom = 0
  elm.style.paddingTop = 0
  elm.style.paddingBottom = 0
  const heightAllLine = elm.offsetHeight
  elm.style.lineHeight = 0
  const height1line = elm.offsetHeight
  const lineCount = Math.round(heightAllLine / height1line)
  elm.setAttribute('style', style)
  if (isNaN(lineCount)) return 0
  return lineCount
}
Yukulélé
sumber