使用JavaScript设置元素宽度出现不符合预期的结果
Hey there! Let's figure out why your carousel's image holder widths aren't matching up as expected. I spot a few key issues in your code that are causing this discrepancy—let's break them down and fix them step by step.
Key Issues Identified
1. Unreliable Element Selection with childNodes
childNodes includes text nodes (like whitespace from line breaks in your HTML), so this.selector.childNodes[1] might not always target the .rotater div you expect. This means you could be applying styles to unintended nodes, leading to inconsistent sizing.
2. String vs. Number Conversion for data-view-number
The dataset API returns all values as strings. When you divide the parent width by this.number (a string), JavaScript does implicit conversion, but this can lead to unexpected results. It's better to explicitly convert it to an integer.
3. Timing of Layout Calculations
When you set the parent's style and immediately call getBoundingClientRect(), the browser might not have finished reflowing the layout yet. This means you're calculating widths based on the old layout, not the updated one.
4. getBoundingClientRect() vs. clientWidth
getBoundingClientRect().width includes padding, border, and scrollbars (if present) for the element. If your parent carousel has padding or borders, this will throw off your child width calculations. clientWidth gives you the content width plus padding (excluding border and scrollbars), which is more reliable for this use case.
Fixed Code Implementation
Let's adjust your code to address these issues:
Updated JavaScript
class Carousel{ constructor(selector=".carousel"){ const defaultNumber = 5; const defaultAutoscroll = 'off'; const defaultWidth = "100%"; const defaultHeight = "200px"; this.selector = document.querySelector(selector); // Explicitly convert data-view-number to integer this.number = this.selector.dataset.viewNumber ? parseInt(this.selector.dataset.viewNumber, 10) : defaultNumber; this.autoScroll = this.selector.dataset.autoscroll || defaultAutoscroll; this.width = this.selector.dataset.width || defaultWidth; this.height = this.selector.dataset.height || defaultHeight; } makeCarousel(){ let srcList = []; let str = "<div class='previous'><p>←</p></div><div class='rotater'>"; let src; for (let i of this.selector.children){ src = i.getAttribute("src"); srcList.push(src); str += `<div class='carousel-img-holder'><img src=${src} class='carousel-img' /></div>`; } str += "</div><div class='next'><p>→</p></div>"; this.selector.innerHTML = str; // Style the parent carousel const styler = this.selector.style; styler.width = this.width; styler.height = this.height; styler.display = "flex"; // Use querySelector to reliably target the rotater div const rotater = this.selector.querySelector('.rotater'); rotater.style.display = "flex"; rotater.style.height = "100%"; rotater.style.width = "100%"; rotater.style.overflow = "scroll"; // Wait for the browser to finish layout before calculating widths requestAnimationFrame(() => { const carouselWidth = this.selector.clientWidth; const imgHolderWidth = (carouselWidth / this.number) + "px"; const imgHolderHeight = this.selector.clientHeight + "px"; // Target only the carousel-img-holder elements const imgHolders = rotater.querySelectorAll('.carousel-img-holder'); imgHolders.forEach(holder => { holder.style.height = imgHolderHeight; holder.style.width = imgHolderWidth; const img = holder.querySelector('.carousel-img'); img.style.maxWidth = "100%"; img.style.maxHeight = "100%"; // Fixed duplicate maxWidth line! }); // Log values after layout is updated console.log("Width from getBoundingClientRect: "+document.querySelector(".carousel-img-holder").getBoundingClientRect().width) console.log("Width from css style: "+document.querySelector(".carousel-img-holder").style.width) }); } } const carousel = new Carousel("#carousel"); carousel.makeCarousel();
Key Changes Explained
- Reliable Element Selection: Replaced
childNodeswithquerySelectorandquerySelectorAllto target exactly the elements we need, avoiding text node issues. - Explicit Type Conversion: Converted
data-view-numberto an integer to ensure accurate division. - Layout Timing: Wrapped the width calculation in
requestAnimationFrameto ensure we only calculate sizes after the browser has applied the parent styles and completed reflow. - Consistent Sizing: Used
clientWidthandclientHeightfor the parent carousel to get accurate content dimensions, ensuring child widths are calculated correctly. - Fixed Duplicate Line: Removed the duplicate
img.style.maxWidthline and replaced it withmaxHeightto properly constrain images.
Additional Tips
- To prevent horizontal scrollbar issues in the rotater, you can add
rotater.style.scrollbarWidth = "none";(for Firefox) androtater.style.msOverflowStyle = "none";(for IE), plus a CSS pseudo-element for Chrome/Safari if needed. - Consider adding
box-sizing: border-box;to all elements via CSS to ensure padding and borders don't affect your width calculations:* { box-sizing: border-box; }
This should resolve the mismatch between the applied style width and the getBoundingClientRect() value, giving you a carousel that correctly displays the number of images specified in data-view-number.
内容的提问来源于stack exchange,提问作者Yuki.kuroshita

