简体   繁体   中英

Dynamically set scroll coordinates & change DOM element with 200px scroll tolerance from these coordinates

I've got a single page site with two div modules that expand when clicked.

The first is at the top, in the Hero section and with the current code works as desired. The second is much further down the page in another section. Currently this second module closes as soon as the scroll takes place.

What I need to do is get the documents scroll coordinates when the div is clicked. Then once the user scrolls 200px up or down the div closes back up. Regardless of where it (the div ) is on the site.

All the questions I found here and elsewhere only refer to setting the scroll tolerance relative to the window position on page load. But for me that's no goon. This site is responsive, and as it changes the initial positions of the div 's will / can be unknown. I need someway of dynamically storing the viewports scroll position when the div 's are clicked and assigning the 200px tolerance then.

I hope that makes sense. I've been at this for like 12+hrs now. SOS :)

Here's the Fiddle

If you don't want to go over to Fiddle , Here's the requisite code

HTML:

<body>
  <section id="hero">
    <div>
      <div class="module-cta hero-cta">
        <a class="module-cta__button"><!-- Fallback location -->
          <span class="module-cta__text">PRESS ME</span>
        </a>
        <div class="module-cta__open">
          <div class="module-cta__open-inner">
            <div class="hero-cta__contact-points">
              <div class="phone">
                <div class="hero-cta_contact_logo">
                  <span><!-- phone.svg" --></span>
                </div><!-- .service-logo -->
                <div class="contact_trigger">
                  <a>Scroll down to 200px to see</a>
                </div><!-- .contact_trigger -->
              </div><!-- .phone -->
              <div class="email">
                <div class="hero-cta_contact_logo">
                  <span><!-- email.svg --></span>
                </div><!-- .service-logo -->
                <div class="contact_trigger">
                  <a>this div fold back up</a>
                </div><!-- .contact_trigger -->
              </div><!-- .email -->
            </div><!-- .hero-cta__contact-points -->
            <button class="module-cta__close module-cta__cancel"><i class="icon"><span></span></i></button>
          </div><!-- .hero-cta__open-inner -->
        </div><!-- .hero-cta__open -->
      </div><!-- .hero-cta -->
    </div>
  </section>
  <section class="spacer"></section>
  <section id="service_area">
    <div class="area_input">
      <div class="module-cta area-cta wow fadeInUp" id="form_module">
        <a class="module-cta__button area-cta__button">
          <span class="module-cta__text area-cta__text">NOW PRESS ME</span>
        </a>
        <div class="module-cta__open area-cta__open">
          <div class="module-cta__open-inner area-cta__open-inner">
            <div class="area-cta__search">
              <form class="postcode_form" id="postcode_form" name="postcode_form" action="#">
                <input type="number" id="your_postcode" class="your_postcode" name="postcode" placeholder="3???">
                <button type="button" class="area-btn"><span></span></button>
                <a class="call-now">##########</a>
              </form>
            </div><!-- .area-cta__search -->
            <button class="module-cta__close module-cta__cancel"><i class="icon"><span></span></i></button>
          </div><!-- .area-cta__open-inner -->
        </div><!-- .area-cta__open -->
      </div><!-- .area-cta -->
    </div><!-- .area_input -->
  </section>
  <section class="spacer"></section>
</body>

Script: I'm sure a lot of this can be cleaned up and shrunk, but for now I'm just trying to get it all going.

// opens & closes modules by clicking module name
$('.module-cta__button').on('click', function(){
  if($(this).parent().hasClass('hero-cta')){
    $(this).parent().toggleClass('module-cta--active');
  } else {
    if($(this).parent().hasClass('area-cta')){
      $(this).parent().toggleClass('module-cta--active');
    }
  }
});

// closes modules with .module-cta__close btn
$('.module-cta__close').on('click', function(){
  if($(this).closest('div .module-cta').hasClass('module-cta--active')){
    $(this).closest('div .module-cta').removeClass('module-cta--active');
  }
});

// closes modules on scroll.
// * works but doesn't apply scroll tolerance of 200px for #area
$(window).scroll(function(){
  var currentPos = $(window).scrollTop();
  var module = $('div .module-cta');
  if(module.hasClass('module-cta--active') && module.position().top <= currentPos+200){
    $('div .module-cta--active').removeClass('module-cta--active');
  }
});


// closes modules when escape key is pressed
$(window).keydown(function(escape){
  var key = escape.which;
  if(key == 27){
    $('div .module-cta--active').removeClass('module-cta--active');
  }
});

see Fiddle for css

  • Thanks for any help or useful suggestions in advanced.

So I've put together a script that will potentially help you.

I've done some basic testing on it, but if you come across any problems make a comment.

// Generate offsets and return them as an object
function generateOffsets($element, tolerance)
{
  var offsets = $element.offset(),
      offsetTop = offsets.top;

  return {
    scrollPos: offsetTop,
    toleranceTop: offsetTop - tolerance,
    toleranceBottom: offsetTop + tolerance
  };
}

// Run a callback when the user leaves the scroll tolerance of a set of elements
function closeOnScroll($elements, tolerance, callback)
{
  $elements.each(function() {
    var $element = $(this),
        offsets = generateOffsets($element, tolerance),
        resizeEvent;

    // On resize, regenerate the offsets so they stay up to date
    $(window).on('resize', function(e) {
      resizeEvent = e;
      offsets = generateOffsets($element, tolerance);
    });

    // On scroll check if we've left the tolerance area, if so run the event and unbind
    $(window).on('scroll', function(e) {
      var windowPos = $(this).scrollTop();

      if (windowPos < offsets.toleranceTop || windowPos > offsets.toleranceBottom) {
        callback($element);
        $(this).unbind(e);
        $(this).unbind(resizeEvent);
      }
    }); 
  });
}

// Example: Apply the event to a set of elements
$('.btn').click(function() {
  closeOnScroll($('div .module-cta'), 200, function($element) {
    $element.removeClass('module-cta--active');
  });
});

The thing to remember with thie script is that it needs to be applied EVERY time the user clicks your button. You may say, WHY WOULD YOU DO THAT - but it actually has some serious performance implications.

The events rely on both scroll and resize, both of which are very slow, especially if not debounced like in this script. However, what I do in the script is unbind the events once it has occured. Otherwise, the resize and scroll would keep happening for each and every one of your buttons until the end of time. By 'unbinding' the event, you ensure long running performance of your page.

Wouldn't want to ruin those pretty animations would we?

I've put together a much smaller and simpler demonstration, just to show you what variables you need to accomplish this. Essentially, when a div is clicked, capture the current document scroll position using $(document).scrollTop() . Also store a refference to the current div that has been clicked.

When scrolling, check the difference between the current scroll and the new scroll, and using your clicked div reference, shrink the div when the difference is 200 or greater. JS fiddle below;

https://jsfiddle.net/jLqu4pas/

Code from Fiddle;

var currentScroll;
var lastClickedDiv;

$('section').click(function(){
  $(this).css({'height' : '400'})
  currentScroll = $(document).scrollTop();
  lastClickedDiv = $(this);

  console.log(currentScroll);
})

$(window).scroll(function(){
  if($(document).scrollTop() > currentScroll + 200){
    lastClickedDiv.css({'height' : 0})
  }
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM