Viewed   5.6k times

I have this code for my website:

function clickMe() {
  var element = document.getElementById('about');
  element.scrollIntoView({
    block: 'start',
    behavior: 'smooth',
  });
}

This works pretty nice but I have a fixed header so when the code scrolls to the element the header is in the way.

Is there a way to have an offset and make it scroll smoothly?

 Answers

3

Is there a way to have an offset and make it scroll smoothly?

Yes, but not with scrollIntoView()

The scrollIntoViewOptions of Element.scrollIntoView() do not allow you to use an offset. It is solely useful when you want to scroll to the exact position of the element.

You can however use Window.scrollTo() with options to both scroll to an offset position and to do so smoothly.

If you have a header with a height of 30px for example you might do the following:

function scrollToTargetAdjusted(){
    var element = document.getElementById('targetElement');
    var headerOffset = 45;
    var elementPosition = element.getBoundingClientRect().top;
    var offsetPosition = elementPosition - headerOffset;

    window.scrollTo({
         top: offsetPosition,
         behavior: "smooth"
    });
}

This will smoothly scroll to your element just so that it is not blocked from view by your header.

Note: You substract the offset because you want to stop before you scroll your header over your element.

See it in action

You can compare both options in the snippet below.

<script type="text/javascript">
  function scrollToTarget() {

    var element = document.getElementById('targetElement');
    element.scrollIntoView({
      block: "start",
      behavior: "smooth",
    });
  }

  function scrollToTargetAdjusted() {
    	var element = document.getElementById('targetElement');
      var headerOffset = 45;
    	var elementPosition = element.getBoundingClientRect().top;
      var offsetPosition = elementPosition - headerOffset;
      
      window.scrollTo({
          top: offsetPosition,
          behavior: "smooth"
      });   
  }

  function backToTop() {
    window.scrollTo(0, 0);
  }
</script>

<div id="header" style="height:30px; width:100%; position:fixed; background-color:lightblue; text-align:center;"> <b>Fixed Header</b></div>

<div id="mainContent" style="padding:30px 0px;">

  <button type="button" onclick="scrollToTarget();">element.scrollIntoView() smooth, header blocks view</button>
  <button type="button" onclick="scrollToTargetAdjusted();">window.scrollTo() smooth, with offset</button>

  <div style="height:1000px;"></div>
  <div id="targetElement" style="background-color:red;">Target</div>
  <br/>
  <button type="button" onclick="backToTop();">Back to top</button>
  <div style="height:1000px;"></div>
</div>
Saturday, September 3, 2022
4

It is supported yes, but user experience is... bad.

As @9bits pointed out, this has long been supported by all major browsers. Not to worry about that. The main problem is the way that it works. It simply jumps to a particular element that may as well be at the end of the page. By jumping to it, users have no idea whether:

  • page has been scrolled up
  • page has been scrolled down
  • they've been redirected elsewhere

The first two can be determined by scroll position, but who says users kept track of scroll position before jump was done? So it's an nondeterministic action.

The last one may be true especially if the page has moving header that gets scrolled out of view and remaining page design doesn't imply anything on being on the same page (if it also doesn't have any total height vertical element like left menu bar). You'd be surprised how many pages have this problem. just check them out yourself. Go to some page, look at it at top, then press End key and look at it again. It is likely that you'll think it's a different page.

Animated scrollintoview jQuery plugin to the rescue

That's why there still are plugins that perform scroll into view instead of using native DOM function. They usually animate scrolling which eliminates all 3 issues outlined above. Users can easily keep track of the movement.

Tuesday, November 8, 2022
 
pooya72
 
5

The block option decides where the element will be vertically aligned inside the visible area of its scrollable ancestor:

  • Using {block: "start"}, the element is aligned at the top of its ancestor.
  • Using {block: "center"}, the element is aligned at the middle of its ancestor.
  • Using {block: "end"}, the element is aligned at the bottom of its ancestor.
  • Using {block: "nearest"}, the element:
    • is aligned at the top of its ancestor if you're currently below it.
    • is aligned at the bottom of its ancestor if you're currently above it.
    • stays put, if it's already in view.

The inline option decides where the element will be horizontally aligned inside the visible area of its scrollable ancestor:

  • Using {inline: "start"}, the element is aligned at the left of its ancestor.
  • Using {inline: "center"}, the element is aligned at the centre of its ancestor.
  • Using {inline: "end"}, the element is aligned at the right of its ancestor.
  • Using {inline: "nearest"}, the element:
    • is aligned at the left of its ancestor if you're currently on its right.
    • is aligned at the right of its ancestor if you're currently on its left.
    • stays put, if it's already in view.

Both block and inline can be used at the same time to scroll to a specified point in one motion.

Check out the following snippet to see how each works in action.

Snippet:

/* ----- JavaScript ----- */
var buttons = document.querySelectorAll(".btn");

[].forEach.call(buttons, function (button) {
  button.onclick = function () {
    var where = this.dataset.where.split("-");
    document.querySelector("div#a1").scrollIntoView({
      behavior: "smooth",
      block: where[0],
      inline: where[1]
    });
  };
});
/* ----- CSS ----- */
body {
  padding: 500px;
  width: 2000px;
}

header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100;
}

div#a1 {
  width: 1000px;
  height: 300px;
  background: url(//www.w3schools.com/css/trolltunga.jpg);
  background-repeat: no-repeat;
}
<!----- HTML ----->
<header>
  <button class="btn" data-where="start-start">T-L</button>
  <button class="btn" data-where="start-center">T-C</button>
  <button class="btn" data-where="start-end">T-R</button>
  <button class="btn" data-where="center-start">C-L</button>
  <button class="btn" data-where="center-center">C-C</button>
  <button class="btn" data-where="center-end">C-R</button>
  <button class="btn" data-where="end-start">B-L</button>
  <button class="btn" data-where="end-center">B-C</button>
  <button class="btn" data-where="end-end">B-R</button>
</header>

<div id = "a1"></div>
Wednesday, August 17, 2022
 
4

Extracted the scrolling logic into its own function, which accepts an element's id as an argument.

//Smoothy scroll to top
$back_to_top.on('click', function(event) {
    event.preventDefault();
    targetedScroll();
});

// example of smooth scroll to h2#anchor-name
$('#some-button').on('click', function(event) {
    event.preventDefault();
    targetedScroll('anchor-name');
});

// bind smooth scroll to any anchor on the page
$('a[href^="#"]').on('click', function(event) {
    event.preventDefault();
    targetedScroll($(this).attr('href').substr(1));
});

// scrolling function
function targetedScroll(id) {
    // scrollTop is either the top offset of the element whose id is passed, or 0
    var scrollTop = id ? $('#' + id).offset().top : 0;

    $('body,html').animate({
        scrollTop: scrollTop,
    }, scroll_top_duration);
}
Friday, September 9, 2022
 
1

Element.scrollIntoView()

Element.scrollIntoView() method scrolls the element on which it's called into the Viewport of the browser window.


Syntax

  • element.scrollIntoView()
  • element.scrollIntoView(alignToTop) // Boolean parameter
  • element.scrollIntoView(scrollIntoViewOptions) // Object parameter

Parameters

The parameters for this method are:

  • alignToTop (Optional): Is a Boolean value, if true, the top of the element will be aligned to the top of the visible area of the scrollable ancestor. This is the default value. If false, the bottom of the element will be aligned to the bottom of the visible area of the scrollable ancestor. Corresponds to scrollIntoViewOptions: {block: "end", inline: "nearest"}.
  • scrollIntoViewOptions (Optional): Is an Object with the following properties:
    • behavior (Optional): Defines the transition animation. One of "auto" or "smooth". Defaults to "auto".
    • block (Optional): Defines vertical alignment. One of "start", "center", "end", or "nearest". Defaults to "start".
    • inline (Optional): Defines horizontal alignment. One of "start", "center", "end", or "nearest". Defaults to "nearest".

This usecase

As per your line of code:

((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", element);

the argument true refers to boolean value for alignToTop. Hence the issue.


Solution

To automate horizontal scrolling you need to pass the argument for the inline parameter either among the following: - start - centre - end - nearest


Alternative

As an alternative you can use either of the following options

  • scrollLeft(): Element.scrollLeft() property gets or sets the number of pixels that an element's content is scrolled from its left edge. If the element's direction is rtl (right-to-left), then scrollLeft is 0 when the scrollbar is at its rightmost position (at the start of the scrolled content), and then increasingly negative as you scroll towards the end of the content.
  • scrollWidth(): Element.scrollWidth() read-only property is a measurement of the width of an element's content, including content not visible on the screen due to overflow.

Outro

You can find a couple of relevant detailed discussion in:

  • What is the difference between the different scroll options?
  • Selenium python Error: element could not be scrolled into view
Saturday, September 17, 2022
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :