FormControlRange-geometry-basic.html (6533B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <script src="/resources/testharness.js"></script> 4 <script src="/resources/testharnessreport.js"></script> 5 <body></body> 6 <script> 7 'use strict'; 8 const controls = ['input','textarea']; 9 10 function setupControl(control, value) { 11 document.body.innerHTML = control === 'input' ? '<input type="text" id="test">' 12 : '<textarea id="test"></textarea>'; 13 const element = document.getElementById('test'); 14 element.value = value; 15 element.focus(); 16 // Stabilize layout. 17 element.style.fontFamily = 'monospace'; 18 element.style.fontSize = '16px'; 19 element.style.lineHeight = '20px'; 20 element.style.padding = '0'; 21 element.style.border = '0'; 22 element.style.margin = '8px'; 23 element.style.boxSizing = 'content-box'; 24 25 // Zero scroll offsets so client rects are relative to a known origin. 26 if ('scrollTop' in element) element.scrollTop = 0; 27 if ('scrollLeft' in element) element.scrollLeft = 0; 28 return element; 29 } 30 function setupFormControlRange(element, startOffset, endOffset){ 31 const range = new FormControlRange(); 32 range.setFormControlRange(element, startOffset, endOffset); 33 return range; 34 } 35 36 function assert_rect_inside(inner, outer, msg = '') { 37 const rounding = 0.5; // sub-pixel fuzz 38 assert_greater_than_equal(inner.left + rounding, outer.left, msg + 'left inside'); 39 assert_less_than_equal(inner.right - rounding, outer.right, msg + 'right inside'); 40 } 41 42 controls.forEach(controlType => { 43 test(() => { 44 // Collapsed range (caret): no client rects; bounding rect is a caret box 45 // (zero width, non-zero height) positioned within the control. 46 const element = setupControl(controlType, 'Hello'); 47 const range = setupFormControlRange(element, 2, 2); 48 const caretRect = range.getBoundingClientRect(); 49 assert_equals(range.getClientRects().length, 0, 'collapsed: no client rects'); 50 assert_approx_equals(caretRect.width, 0, 0.05, 'caret width should be 0'); 51 assert_greater_than(caretRect.height, 0, 'caret height greater than 0'); 52 assert_rect_inside(caretRect, element.getBoundingClientRect(), 'caret inside '); 53 }, `Collapsed caret geometry (${controlType})`); 54 55 test(() => { 56 // Non-collapsed selection: non-zero geometry, all rects contained in control. 57 const element = setupControl(controlType, 'ABCDE'); 58 const range = setupFormControlRange(element, 1, 4); 59 const boundingRect = range.getBoundingClientRect(); 60 const clientRects = Array.from(range.getClientRects()); 61 assert_greater_than(boundingRect.width, 0, 'selection width greater than 0'); 62 assert_greater_than(boundingRect.height, 0, 'selection height greater than 0'); 63 assert_rect_inside(boundingRect, element.getBoundingClientRect()); 64 assert_greater_than_equal(clientRects.length, 1); 65 clientRects.forEach((clientRect, index) => 66 assert_rect_inside(clientRect, element.getBoundingClientRect(), 'rect[' + index + '] ') 67 ); 68 }, `Simple selection geometry (${controlType})`); 69 70 test(() => { 71 // If the control is removed from the DOM, geometry should be empty. 72 const element = setupControl(controlType, 'ABCDE'); 73 const range = setupFormControlRange(element, 0, element.value.length); 74 assert_greater_than(range.getBoundingClientRect().width,0,'pre removal width greater than 0'); 75 element.remove(); 76 const boundingRect = range.getBoundingClientRect(); 77 assert_approx_equals(boundingRect.width, 0, 0.05, 'width should be 0 after removal'); 78 assert_equals(range.getClientRects().length, 0); 79 }, `Geometry empty after control removal (${controlType})`); 80 81 test(() => { 82 const value = 'ABCDE'; 83 const element = setupControl(controlType, value); 84 [0, Math.floor(value.length / 2), value.length].forEach(position => { 85 const range = setupFormControlRange(element, position, position); 86 const caretRect = range.getBoundingClientRect(); 87 assert_equals(range.getClientRects().length, 0, 'collapsed caret has no rects'); 88 assert_approx_equals(caretRect.width, 0, 0.05, 'caret width should be 0'); 89 assert_greater_than(caretRect.height, 0, 'caret height greater than 0'); 90 }); 91 }, `Collapsed caret at start/middle/end (${controlType})`); 92 93 test(() => { 94 // Full selection should yield a non-zero bounding box inside the control. 95 // (For textarea, the string includes a hard newline to exercise multi-line.) 96 const value = controlType === 'textarea' ? 'First line\nSecond line' : 'ABCDE'; 97 const element = setupControl(controlType, value); 98 const range = setupFormControlRange(element, 0, element.value.length); 99 const boundingRect = range.getBoundingClientRect(); 100 assert_greater_than(boundingRect.width, 0, 'full width greater than 0'); 101 assert_greater_than(boundingRect.height, 0, 'full height greater than 0'); 102 assert_rect_inside(boundingRect, element.getBoundingClientRect(), 'full selection inside'); 103 }, `Full selection bounding box inside element (${controlType})`); 104 105 test(() => { 106 // Backwards offsets are auto-collapsed by setFormControlRange; caret geometry applies. 107 document.body.innerHTML = controlType === 'input' ? '<input type="text" value="Test">' 108 : '<textarea>Test</textarea>'; 109 const element = document.body.firstElementChild; const range = new FormControlRange(); 110 range.setFormControlRange(element, 3, 1); 111 assert_true(range.collapsed, 'collapsed'); 112 const caretRect = range.getBoundingClientRect(); 113 assert_approx_equals(caretRect.width, 0, 0.05, 'caret width should be 0'); 114 assert_greater_than(caretRect.height, 0, 'caret height greater than 0'); 115 }, `Backwards offsets collapse (${controlType})`); 116 117 test(() => { 118 // Elements with display:none have no rendered geometry; ranges report empty rects. 119 document.body.innerHTML = controlType === 'input' 120 ? '<input type="text" id="displayNone" style="display:none" value="hidden">' 121 : '<textarea id="displayNone" style="display:none">hidden</textarea>'; 122 const element = document.getElementById('displayNone'); 123 const range = setupFormControlRange(element, 0, element.value.length); 124 const boundingRect = range.getBoundingClientRect(); 125 assert_approx_equals(boundingRect.width, 0, 0.05, 'width should be 0 (display:none)'); 126 assert_approx_equals(boundingRect.height, 0, 0.05, 'height should be 0 (display:none)'); 127 assert_equals(range.getClientRects().length, 0); 128 }, `display:none empty geometry (${controlType})`); 129 }); 130 </script>