Home > Back-end >  Difference in getClientRects() results across browsers
Difference in getClientRects() results across browsers

Time:03-18

I'm using the method getClientRects() in one of my JavaScript function. The function works correctly in Chrome, Edge and Safari but fails in Firefox. The reason is that getClientRects() behaves differently in Firefox.

The sample is available here in CodePen

var el = document.getElementById('parent');
var range = document.createRange();

range.selectNode(el);
var clientRects = range.getClientRects();
console.log(clientRects);
<div  id='parent'>
    <div >Bacon ipsum dolor amet meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham.
    </div>
    <div id="info">Click somewhere in the paragraph above</div>
</div>

Run the sample in Chrome and Firefox to see the difference. Is this a bug in Firefox?

I tried to refer the documentation for any insights but unable to get clarity.

CodePudding user response:

If your question is why you only get one element in the result from Firefox but multiple ones from Chrome, that's answered in the documentation:

Originally, Microsoft intended this method to return a TextRectangle object for each line of text. However, the CSSOM working draft specifies that it returns a DOMRect for each border box. For an inline element, the two definitions are the same. But for a block element, Mozilla will return only a single rectangle.

You're logging a block element. So Chrome is doing what the CSSOM working draft says, and Firefox is doing what they interpret Microsoft originally meant. If/when the working draft progresses, you might call it a bug in one or the other, depending on what the CSSOM document then specifies.

CodePudding user response:

This would be a Chrome Safari bug.

The specs ask that when the Range#getClientRects() method is called,

For each element selected by the range, whose parent is not selected by the range, include the border areas returned by invoking getClientRects() on the element.

In your Range, there is only a single element whose parent is not selected by the range: #parent.

So we are supposed to get a DOMRectList composed of #parent's DOMRect. And for this, all browsers do agree, there is only one such DOMRect for this element:

var el = document.getElementById("parent");
// Element#getClientRects();
var clientRects = el.getClientRects();
console.log(clientRects.length); // 1 everywhere
<div  id='parent'>
    <div >Bacon ipsum dolor amet meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham.
    </div>
    <div id="info">Click somewhere in the paragraph above</div>
</div>

If you did select the content of this element instead, then that should be two DOMRects, the one of .child and the one of #info (and possibly the ones of the empty text nodes, but I believe there are arguments about that). And indeed, in Firefox what we get 2 rects:

var el = document.getElementById('parent');
var range = document.createRange();
// select the content, not the node itself
range.selectNodeContents(el);
var clientRects = range.getClientRects();
console.log(clientRects.length); // 2 in Firefox
<div  id='parent'>
    <div >Bacon ipsum dolor amet meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham.
    </div>
    <div id="info">Click somewhere in the paragraph above</div>
</div>

And if you went even farther and selected the content of .child, you'd finally get one box per line:

var el = document.querySelector('.child');
var range = document.createRange();
// select the content, not the node itself
range.selectNodeContents(el);
var clientRects = range.getClientRects();
console.log(clientRects.length); // all browsers agree, 1 box per line
<div  id='parent'>
    <div >Bacon ipsum dolor amet meatball bresaola t-bone tri-tip brisket. Jowl pig picanha cupim landjaeger, frankfurter spare ribs chicken. Porchetta jowl pancetta drumstick shankle cow spare ribs jerky tail kevin biltong capicola brisket venison bresaola. Flank sirloin jowl andouille meatball venison salami ground round rump boudin turkey capicola t-bone. Sirloin filet mignon tenderloin beef, biltong doner bresaola brisket shoulder pork loin shankle turducken shank cow. Bacon ball tip sirloin ham.
    </div>
    <div id="info">Click somewhere in the paragraph above</div>
</div>

So it seems that Chrome and Safari do search for all the text nodes (even the empty ones) and produce one DOMRect per line in these text nodes, against what the standards say to do.
Weirdly enough I couldn't find any report of this interop issue.

  • Related