Как использовать scrollIntoView с переполнением: скрытым и без прокрутки страницы?

#javascript #html #css

#javascript #HTML #css

Вопрос:

Как использовать scrollIntoView в контейнере, который имеет overflow:hidden и не должен прокручивать страницу?

Вот пример: внизу страницы текст в контейнере <div class="cnt'> с фиксированной шириной и скрытым переполнением. Я хочу прокручивать элементы в этом контейнере без прокрутки страницы.

В верхней части страницы две кнопки для прокрутки до первого и последнего элемента. Если я нажму на кнопку, она будет прокручивать текст в контейнере и переходить к этому контейнеру внизу страницы.

Я не могу использовать scrollLeft, потому что переполнение скрыто. 🙁

Кто-нибудь знает, как это решить?

 const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]

lastSpan.scrollIntoView()

const buttons = Array.from(document.querySelectorAll('button')) 
const [buttonToFirstEl, buttonToLastEl] = buttons;

buttonToFirstEl.onclick = function() {
  firstSpan.scrollIntoView()
}

buttonToLastEl.onclick = function() {
  lastSpan.scrollIntoView()
}  
 .cnt {
  width: 90px;
  overflow: hidden;
  white-space: nowrap;
  padding: 8px;
  border: solid #ccc 1px;
}

.filler {
  width: 100%;
  height: 200px;
  border: dashed 2px #ccc;
  margin: 20px;
}

.root {
  border: solid 1px;
}  
 <div class="root">
  <button id="button">scroll to first element</button>
  <button id="button">scroll to last element</button>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="cnt">
    <span>
      first:tessst
    </span>
    <span>
      2:dddd
    </span>
    <span>
      3:cccddd
    </span>
    <span>
      4:rreeee
    </span>
    <span>
      last:dddrreddd
    </span>
  </div>
</div>  

https://codepen.io/geeny273/pen/bGpxYqG

Комментарии:

1. вы хотите, чтобы в div .root была скрыта прокрутка Y с переполнением, верно

2. Вы хотите только поведение scrollIntoView по умолчанию или также иметь возможность передавать правила, встроенные в блок? Кроме того, вам важно писать указания или обрабатывать все как слева направо сверху вниз, это нормально?

3. @OuchaneCC Я хочу прокручивать элементы внутри <div class=»cnt»> . .корневой элемент, используемый для отображения этой страницы, тоже прокручивается

4. @Kaiido для этого примера поведение по умолчанию в порядке

Ответ №1:

если вы хотите прокручивать элемент в классе .cnt

введите описание изображения здесь

  • вам нужно стилизовать каждый интервал в .cnt

.cnt span{

      /* relative   block for make elements solid amp; listed*/
      position : relative;
      display : block;
  
       color: red;
      font-size : 16px; 
  

}

  • и в родительском файле .cnt вам нужно указать высоту, чтобы прокрутка работала

.cnt {

   width: 90px;

  /* for example 28px */
  height : 28px;

  overflow: hidden;
  white-space: nowrap;
  padding: 4px;
  border: solid black 1px;

  /* overflow y or x for making scroll*/
  overflow-y : scroll;
  

}

  • я надеюсь, что эти шаги помогут вам

Ответ №2:

Чтобы получить это правильно (т.Е. С поддержкой всех параметров режима записи, направления и встроенного блока), вам, по сути, придется переписать scrollIntoView с нуля, опустив цикл, который поднимается по дереву прокрутки. коробки. Это не тривиальная задача.

Если вам не нужно пуленепробиваемое решение, вы можете просто получить ближайшее окно прокрутки и проверить, насколько оно должно быть прокручено, чтобы показать начало вашего элемента:

 const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]



const buttons = Array.from(document.querySelectorAll('button')) 
const [buttonToFirstEl, buttonToLastEl] = buttons;

buttonToFirstEl.onclick = function() {
  scrollIntoParentView( firstSpan );
}

buttonToLastEl.onclick = function() {
  scrollIntoParentView( lastSpan );
}

function scrollIntoParentView( elem, options ) {
  const directions = getSimpleScrollIntoViewDirections( elem, options );
  const left = directions.inline_direction || 0;
  const top = directions.block_direction || 0;
  const new_options = {
    left,
    top,
    behavior: (options amp;amp; options.behavior) || "auto"
  };
  directions.scrolling_box.scrollBy( new_options );
}
function getSimpleScrollIntoViewDirections( elem, { block = "start", inline = "start" } = {} ) {

  const element_bbox = elem.getBoundingClientRect();
  const scrolling_box = getNearestScrollingBox( elem );
  const scrolling_box_bbox = scrolling_box.getBoundingClientRect();

  const block_direction = Math.round( element_bbox.top - scrolling_box_bbox.top);
  const inline_direction = Math.round( element_bbox.left - scrolling_box_bbox.left);

  return {
    scrolling_box,
    block_direction,
    inline_direction
  };
}

function getNearestScrollingBox( elem ) {
  if( !elem.isConnected ) { return null; }
  const elem_computed_styles = getComputedStyle( elem );
  // not in specs, but that seems to be what browser implementations do
  if( elem_computed_styles.getPropertyValue( 'position' ) === "fixed" ) {
    return null;
  }
  const scrolling_box = elem.parentElement;
  if( !scrolling_box ) {
    return elem === document.scrollingElement ? null : document.scrollingElement;
  }
  const computed_styles = getComputedStyle( scrolling_box );
  const scroll_x = computed_styles.getPropertyValue( "overflow-x");
  const scroll_y = computed_styles.getPropertyValue( "overflow-y");
  if(
    (scroll_x === 'clip' amp;amp; scroll_y === 'clip') ||
    (scrolling_box.offsetHeight <= scrolling_box.scrollingHeight) ||
    (scrolling_box.offsetWidth <= scrolling_box.scrollingWidth)
   ) {
    return getNearestScrollingBox( scrolling_box );
  }
  return scrolling_box;
}  
 .cnt {
  width: 90px;
  overflow: hidden;
  white-space: nowrap;
  padding: 8px;
  border: solid #ccc 1px;
}

.filler {
  width: 100%;
  height: 200px;
  border: dashed 2px #ccc;
  margin: 20px;
}

.root {
  border: solid 1px;
}  
 <div class="root">
  <button id="button">scroll to first element</button>
  <button id="button">scroll to last element</button>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="cnt">
    <span>
      first:tessst
    </span>
    <span>
      2:dddd
    </span>
    <span>
      3:cccddd
    </span>
    <span>
      4:rreeee
    </span>
    <span>
      last:dddrreddd
    </span>
  </div>
</div>  

Ответ №3:

scrollLeft и scrollTop хорошо работать overflow:hidden с. Я проверяю scrollLeft свойство первого элемента span вместо его контейнера

изменение scrollLeft родительского элемента span, решить эту проблему

  firstSpan.parentElement.scrollLeft = 0;
  

 const cnt = document.querySelector('.cnt')
const spanElements = cnt.querySelectorAll('span');
const lastSpan = spanElements[spanElements.length - 1]
const firstSpan = spanElements[0]

lastSpan.scrollIntoView()

const buttons = Array.from(document.querySelectorAll('button')) 
const [buttonToFirstEl, buttonToLastEl] = buttons;

buttonToFirstEl.onclick = function() {
  firstSpan.parentElement.scrollLeft = 0;
}

buttonToLastEl.onclick = function() {
  const cnt = lastSpan.parentElement;
  cnt.scrollLeft = cnt.scrollWidth;
}  
 .cnt {
  width: 90px;
  overflow: hidden;
  white-space: nowrap;
  padding: 8px;
  border: solid #ccc 1px;
}

.filler {
  width: 100%;
  height: 200px;
  border: dashed 2px #ccc;
  margin: 20px;
}

.root {
  border: solid 1px;
}  
 <div class="root">
  <button id="button">scroll to first element</button>
  <button id="button">scroll to last element</button>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="filler">
  </div>
  <div class="cnt">
    <span>
      first:tessst
    </span>
    <span>
      2:dddd
    </span>
    <span>
      3:cccddd
    </span>
    <span>
      4:rreeee
    </span>
    <span>
      last:dddrreddd
    </span>
  </div>
</div>