簡體   English   中英

使用箭頭鍵水平滾動

[英]Horizontal scroll using arrow keys

我在我構建的頁面上使用水平滾動。 它在一個 div 中,用 class 命名,我想使用箭頭鍵滾動。 為了讓它隨鍵滾動,我需要點擊它的某個地方。

是否可以在第一次加載頁面時直接使用按鍵而不必單擊它?

如果可能的話,我需要箭頭鍵直接起作用並在該特定 div 內啟動水平滾動。

<div id="scroll" tabindex="0">      
        <ul class=“box” >
           <div class=“insidebox >
       */content here using various div to fill the box/*
  </div>
</ul>
</div>


<script type="text/javascript">
document.getElementById("scroll").focus();
document.getElementById("box").focus();
document.getElementById("insidebox").focus();
</script>


#scroll {
    grid-column:1/2;
    grid-row: 2/4;
}

  .box { 
    grid-column:1/2;
    grid-row: 2/4; 
    display: grid; 
    grid-template-columns: repeat(20,1fr);
    overflow-x: scroll; 
    scroll-snap-type: x proximity; 
}

.box::-webkit-scrollbar { width: 0 !important }
.box { overflow: -moz-scrollbars-none; }
.box { -ms-overflow-style: none; }



我需要一個帶有左右箭頭鍵的動畫水平可滾動導航欄,只有在需要時才會顯示(當菜單項多於屏幕可見部分時)。

我找到了這個例子 -- https://codepen.io/mahish/pen/RajmQw -- 但它依賴於 jQuery 的 animation 並且它在設計和執行方面存在缺陷。 我對其進行了改編並在這里提出了一個改進版本:

https://codepen.io/KenACollins/pen/ZErBQQo

HTML:

<div id="menu-wrapper" class="menu-wrapper">
    <ul id="menu" class="menu">
        <li class="item">1</li>
    <li class="item">2</li>
    <li class="item">3</li>
    <li class="item">4</li>
    <li class="item">5</li>
    <li class="item">6</li>
    <li class="item">7</li>
    <li class="item">8</li>
    </ul>

    <div class="arrows">
        <button id="leftArrow" class="left-arrow arrow hidden">
            <
        </button>
        <button id="rightArrow" class="right-arrow arrow">
            >
        </button>
    </div>

</div>

<div class="print" id="print-wrapper-size"><b>Wrapper size:</b> <span></span></div>
<div class="print" id="print-menu-size"><b>Total menu size:</b> <span></span></div>
<div class="print" id="print-menu-invisible-size"><b>Invisible menu size:</b> <span></span></div>
<div class="print" id="print-menu-end-offset"><b>Menu end offset:</b> <span></span></div>
<div class="print" id="print-menu-position"><b>Scroll position:</b> <span>0</span></div>

CSS:

body {
  margin: 3em;
  font-family: Arial, Helvetica, sans-serif;
}

* {
  padding: 0;
  margin: 0;
}

.menu-wrapper {
  position: relative; /* Required for arrow keys to be absolutely positioned child divs inside menu-wrapper parent. */
  width: 500px;
  height: 100px;  /* Intentionally shorter than menu items, to hide horizontal scroll bar. */
  margin: 1em auto;
  border: 1px solid black;
  overflow-x: hidden;
  overflow-y: hidden;
  display: flex;
  align-items: center;
  padding: 0 20px;  /* Inner space on the left and right sides of wrapper must match flexbox gap space between menu items. */
  box-sizing: border-box;
}

ul {
  list-style: none; /* Hide unordered list bullet. */
}

.menu {
  height: 120px;
  /* background: #f3f3f3; */
  box-sizing: border-box;
  white-space: nowrap;
  -webkit-overflow-scrolling: touch;
  position: relative; /* Required for animation. */
  display: flex;
  align-items: center;
  gap: 20px;  /* Flexbox space between menu items must match the left/right padding of menu wrapper. */
}

.menu .item {
  background: #f3f3f3;  /* Weird - Visible items inherit this from .menu but not hidden items# 6-8 when they slide in and can be seen. */
  width: 75px;
  height: 75px;
  outline: 1px dotted gray;
  box-sizing: border-box;
  border-radius: 5px;
  display: flex;            /* Needed to center number in middle of menu item, solution # 1 of 3. */
  align-items: center;      /* Needed to vertically center number in middle of menu item, solution # 2 of 3. */
  justify-content: center;  /* Needed to horizontally center number in middle of menu item, solution # 3 of 3. */
}

.arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  /* width: 3em; Excluding width means that arrow div will only be as wide as it needs to be to contain the < or > characters. */
}

.left-arrow {
  left: 0;
}

.right-arrow {
  right: 0;
}

.hidden {
  display: none;
}

.print {
  margin: auto;
  max-width: 500px;
}

.print span {
  display: inline-block;
  width: 100px;
}

JavaScript:

/**
 This code is based on the following developer's work found here https://codepen.io/mahish/pen/RajmQw with the following improvements:
 o Eliminates dependency on jQuery. Is rewritten in vanilla JavaScript with animation frames.
 o Scrolls to next hidden menu item and stops, does not jump to the far right which would be bad if a lot of menu items in beginning of list became hidden.
 o Has a more realistic design with menu items that are spaced apart and smaller than their container.
 o Uses flexbox for positioning.
 o Increases count of menu items from 4 to 8 to simulate a real need for scrolling.
 o Hides right arrow if all menu items fit in the container eliminating the need for scrolling.
 o Simplifies the code by eliminating functions that are only called once.
 o Has meaningful variable names and lots of comments to explain how it works.
 */

// DOM elements to track.
const leftArrow = document.getElementById('leftArrow');
const rightArrow = document.getElementById('rightArrow');
const menu = document.getElementById('menu');

// Establish unchanging constants and initialize variables.
const menuWrapperSize = document.getElementById('menu-wrapper').offsetWidth; // Unchanging area of the screen where the menu is always visible.
const menuSize = document.getElementById('menu').offsetWidth;   // Includes itemsCount * itemSize but also factors in space between items added by flexbox.
const menuInvisibleSize = Math.max(menuSize - menuWrapperSize, 0);  // Fixed portion of scrollable menu that is hidden at all times, or zero if menu fits within container.
const arrowSize = rightArrow.offsetWidth;   // Width of each arrow div. In current design, this equates to 12px. Still computes value even if right arrow is hidden, which it is at time this line is executed.
const menuEndOffset = Math.max(menuInvisibleSize - arrowSize, 0);   // Fixed portion of scrollable menu that is not obscured by an overlapping arrow key, or zero if no arrow keys are needed.
const itemsCount = document.getElementsByClassName('item').length; // Number of menu items.
const itemSize = document.getElementsByClassName('item')[0].offsetWidth; // offsetWidth includes borders and padding but not margins of a menu item (since all the same, choose first one in array). FYI, clientWidth includes padding but NOT borders and margins.
const itemsSpaceBetween = (menuSize - (itemsCount * itemSize)) / (itemsCount - 1);  // Space between menu items is deliberately set to equal menu wrapper padding left/right. In this design it is 20 pixels.
const distanceInPixels = itemSize + itemsSpaceBetween;  // Distance to scroll per arrow button click equals width of a menu item plus the space to its right or left. In this design, it is 75 + 20 = 95.
const durationInMilliseconds = 500;
let starttime = null;

// Iniitially, on page load menu items are left aligned and left arrow is hidden. Let's hide right arrow also if there is no need for it (as when all menu items fit within visible container).
if (menuInvisibleSize === 0) {
    rightArrow.classList.add("hidden");
}

// Get current left position of menu in pixels.
const getMenuPosition = () => {
    return parseFloat(menu.style.left) || 0;    // First time, left property is not set so initialize to 0.
};

// Get current distance (in pixels) that we have scrolled.
const getScrolledDistance = () => {
    return -1 * getMenuPosition();  // Negate value because this is the only way it will work.
};

// After an arrow key is clicked and menu is animating, check to see where we are and determine which arrow key(s) to show, always resulting in at least one arrow key visible. Also, update data at bottom.
// Notes: o This function is only applicable when all menu items cannot be seen in container at one time and an arrow key is clicked to animate menu. 
//        o If all menu items fit in visible container, UI will be initially rendered without any arrow keys and this function will never be called.
const checkPosition = () => {
    // Calculate where we are right now.
    const menuPosition = getScrolledDistance();

    // Determine which arrow key(s) to display based on position.
    if (menuPosition <= arrowSize) {            // SHOW RIGHT ARROW if we are scrolling from far left.
        leftArrow.classList.add("hidden");      // FYI, this will NOT create duplicate hidden class if leftArrow already contains it.   
        rightArrow.classList.remove("hidden");
    } else if (menuPosition < menuEndOffset) {  // SHOW BOTH ARROWS when in the middle of the menu.
        leftArrow.classList.remove("hidden");
        rightArrow.classList.remove("hidden");
    } else if (menuPosition >= menuEndOffset) { // SHOW LEFT ARROW if we are scrolling as far right as we can go.
        leftArrow.classList.remove("hidden");
        rightArrow.classList.add("hidden");
    }

    // Print changing scroll position under the menu for informational purposes.
    document.querySelector("#print-menu-position span").textContent = menuPosition + 'px';
};

const animateMenu = (timestamp, startingPoint, distance) => {
    const runtime = timestamp - starttime;
    let progress = runtime / durationInMilliseconds;
    progress = Math.min(progress, 1);
    let newValue = (startingPoint + (distance * progress)).toFixed(2) + 'px';
    menu.style.left = newValue;

    if (runtime < durationInMilliseconds) { // If we still have time remaining...
        requestAnimationFrame(function(timestamp) { // Request another animation frame and recursively call THIS function.
            animateMenu(timestamp, startingPoint, distance);
        })
    }
    checkPosition();
};
 
const animationFramesSetup = (timestamp, travelDistanceInPixels) => {
    timestamp = timestamp || new Date().getTime();  // if browser doesn't support requestAnimationFrame, generate our own timestamp using Date.
    starttime = timestamp;
    const startingPoint = getMenuPosition();        // This cannot be defined up top in constants. Need to read current value only during initial setup of arrow button click.
    animateMenu(timestamp, startingPoint, travelDistanceInPixels);
};

rightArrow.addEventListener('click', () => requestAnimationFrame(
    timestamp => animationFramesSetup(timestamp, -1 * distanceInPixels)
));
    
leftArrow.addEventListener('click', () => requestAnimationFrame(
    timestamp => animationFramesSetup(timestamp, distanceInPixels)
));

// Print unchanging values under the menu for informational purposes.
document.querySelector("#print-wrapper-size span").textContent = menuWrapperSize + 'px';
document.querySelector("#print-menu-size span").textContent = menuSize + 'px';
document.querySelector("#print-menu-invisible-size span").textContent = menuInvisibleSize + 'px';
document.querySelector("#print-menu-end-offset span").textContent = menuEndOffset + 'px';

好的 - 這是我的意思的一個例子。 如果我們將 div 設置為tabindex=0並在tbody元素的末尾添加一個腳本,將焦點設置到 div,左/右光標鍵將在 div 滾動條上工作。

 #scrolldiv {height:100px; width:1000px; overflow-x:scroll; white-space: nowrap;}
 <div id="scrolldiv" tabindex=0> The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. </div> <div> The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. </div> <script type="text/javascript"> document.getElementById("scrolldiv").focus(); </script>

要使用 class,這是我的整個測試頁面:

<!DOCTYPE html>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<html>
<head>
<title>Scrolling div on first load</title>

<script type="text/javascript">

// no script needed here for this functionality

</script>

<style type="text/css">

.scrolldiv {height:100px; width:1000px; overflow-x:scroll; white-space: nowrap;}
.firstdiv {}

</style>
</head>
<body>

<div class="scrolldiv firstdiv" tabindex=0>
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. 

</div>

<div class="scrolldiv">
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. 

</div>
<script type="text/javascript">
// Set the focus to the first (and ONLY) div that uses the 'firstdiv' class
// This ensures that we only target one div as there may be many using 'scrolldiv' and these could appear in any order on the page
document.getElementsByClassName("firstdiv")[0].focus();

</script>
</body>
</html>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM