最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Finding where element meets top of scrollable div - Stack Overflow

programmeradmin3浏览0评论

I have a scrollable div container fits multiple "pages" (or div's) inside of it's container.

My goal is to, at any given moment, figure out where inside my red container does it reach the top of my scrollable container. So it can be a constant on scroll event, or a button that triggers this task.

So for example. If I have a absolute div element inside one of my red boxes at top:50px. And if I scroll to where that div element reaches the top of my scrollable container. The trigger should say that I am at 50px of my red container.

I'm having a hard time grasping how to acplish this but I've tried things like:

$("#pageContent").scroll(function(e) {
    console.log($(this).scrollTop());   
});

But it doesn't take into account the separate pages and I don't believe it it pletely accurate depending on the scale. Any guidance or help would be greatly appreciated.

Here is my code and a jsfiddle to better support my question.

Note: If necessary, I use scrollspy in my project so I could target which red container needs to be checked.

HTML

<div id="pageContent" class="slide" style="background-color: rgb(241, 242, 247); height: 465px;">
    <div id="formBox" style="height: 9248.627450980393px;">
        <div class="trimSpace" style="width: 1408px; height: 9248.627450980393px;">
            <div id="formScale" style="width: 816px; -webkit-transform: scale(1.7254901960784315); display: block;">
                <form action="#" id="XaoQjmc0L51z_form" autoplete="off">
                    <div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_1">
                        <div class="formContent">
                            <div class="formBackground">
                                <div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
                            </div>
                        </div>
                    </div>
                    <div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_2">
                        <div class="formContent">
                            <div class="formBackground"><div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">This should still say 50px</div></div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

CSS

#pageContent {
    position:relative;
    padding-bottom:20px;
    background-color:#fff;
    z-index:2;
    overflow:auto;
    -webkit-overflow-scrolling: touch;
    -webkit-transform: translate(0, 0);
    -moz-transform: translate(0, 0);
    -ms-transform: translate(0, 0);
    transform: translate(0, 0);
}
#formBox {
    display: block;
    position: relative;
    height: 100%;
    padding: 15px;
}
.trimSpace {
    display: block;
    position: relative;
    margin: 0 auto;
    padding: 0;
    height: 100%;
    overflow: hidden;
}
#formScale::after {
    display: block;
    content:'';
    padding-bottom:5px;
}
#formScale {
    position:relative;
    width:816px;
    margin:0;
    -webkit-transform-origin: 0 0;
    -moz-transform-origin: 0 0;
    transform-origin: 0 0;
    -ms-transform-origin: 0 0;
}
.formContainer {
    position:relative;
    margin : 0 auto 15px auto;
    padding:0;
}
.formContent {
    position:absolute;
    width:100%;
    height:100%;
}
.formBackground {
    width:100%;
    height:100%;
    background-color:red;
}

JS

var PAGEWIDTH = 816;

$(window).resize(function (e) {
    zoomProject();
    resize();
});

function resize() {
    $("#pageContent").css('height', window.innerHeight - 45 + 'px');
}

function zoomProject() {
    var maxWidth = $("#formBox").width(),
        percent = maxWidth / PAGEWIDTH;
    $("#formScale").css({
        'transform': 'scale(' + percent + ')',
        '-moz-transform': 'scale(' + percent + ')',
        '-webkit-transform': 'scale(' + percent + ')',
        '-ms-transform': 'scale(' + percent + ')'
    });
    $(".trimSpace").css('width', (PAGEWIDTH * percent) + 'px');
    $("#formBox, .trimSpace").css('height', ($("#formScale").height() * percent) + 'px');
}

zoomProject();
resize();

EDIT:

I don't think I am conveying a good job at relaying what I want to acplish.

At the moment there are two .formContainer's. When I scroll #pageContainer, the .formContainer divs move up through #pageContainer.

So what I want to acplish is, when a user clicks the "ME" button or #click (as shown in the fiddle below), I'd like to know where in that particular .formContainer, is it touching the top of #pageContainer.

I do use scroll spy in my real world application so I know which .formContainer is closest to the top. So if you just want to target one .formContainer, that is fine.

I used these white div elements as an example. If I am scrolling #pageContainer, and that white div element is at the top of screen as I am scrolling and I click on "ME", the on click trigger should alert to me that .formContainer is touching the top of #pageContainer at 50px from the top. If, the the red container is just touching the top of #pageContainer, it should say it is 0px from the top.

I hope that helps clear up some misconception.

Here is an updated jsfiddle that shows the kind of action that I want to happen.

I have a scrollable div container fits multiple "pages" (or div's) inside of it's container.

My goal is to, at any given moment, figure out where inside my red container does it reach the top of my scrollable container. So it can be a constant on scroll event, or a button that triggers this task.

So for example. If I have a absolute div element inside one of my red boxes at top:50px. And if I scroll to where that div element reaches the top of my scrollable container. The trigger should say that I am at 50px of my red container.

I'm having a hard time grasping how to acplish this but I've tried things like:

$("#pageContent").scroll(function(e) {
    console.log($(this).scrollTop());   
});

But it doesn't take into account the separate pages and I don't believe it it pletely accurate depending on the scale. Any guidance or help would be greatly appreciated.

Here is my code and a jsfiddle to better support my question.

Note: If necessary, I use scrollspy in my project so I could target which red container needs to be checked.

HTML

<div id="pageContent" class="slide" style="background-color: rgb(241, 242, 247); height: 465px;">
    <div id="formBox" style="height: 9248.627450980393px;">
        <div class="trimSpace" style="width: 1408px; height: 9248.627450980393px;">
            <div id="formScale" style="width: 816px; -webkit-transform: scale(1.7254901960784315); display: block;">
                <form action="#" id="XaoQjmc0L51z_form" autoplete="off">
                    <div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_1">
                        <div class="formContent">
                            <div class="formBackground">
                                <div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>
                            </div>
                        </div>
                    </div>
                    <div class="formContainer" style="width:816px;height:1056px" id="xzOwqphM4GGR_2">
                        <div class="formContent">
                            <div class="formBackground"><div style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">This should still say 50px</div></div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>

CSS

#pageContent {
    position:relative;
    padding-bottom:20px;
    background-color:#fff;
    z-index:2;
    overflow:auto;
    -webkit-overflow-scrolling: touch;
    -webkit-transform: translate(0, 0);
    -moz-transform: translate(0, 0);
    -ms-transform: translate(0, 0);
    transform: translate(0, 0);
}
#formBox {
    display: block;
    position: relative;
    height: 100%;
    padding: 15px;
}
.trimSpace {
    display: block;
    position: relative;
    margin: 0 auto;
    padding: 0;
    height: 100%;
    overflow: hidden;
}
#formScale::after {
    display: block;
    content:'';
    padding-bottom:5px;
}
#formScale {
    position:relative;
    width:816px;
    margin:0;
    -webkit-transform-origin: 0 0;
    -moz-transform-origin: 0 0;
    transform-origin: 0 0;
    -ms-transform-origin: 0 0;
}
.formContainer {
    position:relative;
    margin : 0 auto 15px auto;
    padding:0;
}
.formContent {
    position:absolute;
    width:100%;
    height:100%;
}
.formBackground {
    width:100%;
    height:100%;
    background-color:red;
}

JS

var PAGEWIDTH = 816;

$(window).resize(function (e) {
    zoomProject();
    resize();
});

function resize() {
    $("#pageContent").css('height', window.innerHeight - 45 + 'px');
}

function zoomProject() {
    var maxWidth = $("#formBox").width(),
        percent = maxWidth / PAGEWIDTH;
    $("#formScale").css({
        'transform': 'scale(' + percent + ')',
        '-moz-transform': 'scale(' + percent + ')',
        '-webkit-transform': 'scale(' + percent + ')',
        '-ms-transform': 'scale(' + percent + ')'
    });
    $(".trimSpace").css('width', (PAGEWIDTH * percent) + 'px');
    $("#formBox, .trimSpace").css('height', ($("#formScale").height() * percent) + 'px');
}

zoomProject();
resize();

EDIT:

I don't think I am conveying a good job at relaying what I want to acplish.

At the moment there are two .formContainer's. When I scroll #pageContainer, the .formContainer divs move up through #pageContainer.

So what I want to acplish is, when a user clicks the "ME" button or #click (as shown in the fiddle below), I'd like to know where in that particular .formContainer, is it touching the top of #pageContainer.

I do use scroll spy in my real world application so I know which .formContainer is closest to the top. So if you just want to target one .formContainer, that is fine.

I used these white div elements as an example. If I am scrolling #pageContainer, and that white div element is at the top of screen as I am scrolling and I click on "ME", the on click trigger should alert to me that .formContainer is touching the top of #pageContainer at 50px from the top. If, the the red container is just touching the top of #pageContainer, it should say it is 0px from the top.

I hope that helps clear up some misconception.

Here is an updated jsfiddle that shows the kind of action that I want to happen.

Share Improve this question edited Jun 20, 2020 at 9:12 CommunityBot 11 silver badge asked Mar 4, 2015 at 4:07 bryanbryan 9,39918 gold badges91 silver badges174 bronze badges 9
  • Why re-invent the wheel. Just use plugins like ScrollMagic.js or Waypoints.js. Here's a great tutorial. – Miro Commented Mar 6, 2015 at 7:34
  • Thank you, but I am not trying to trigger when an element is hitting the top of the page. It was just used as an example of what should be returned. I just want to know where formContainer is in relation to the top of pageContent. @miro – bryan Commented Mar 6, 2015 at 14:29
  • And then agian miro, re-inventing the wheel in case of a jquery plugin in. Please do, most are terrible. – Patrick Aleman Commented Mar 6, 2015 at 15:19
  • Element.getBoundingClientRect() may help. It returns the size of an element and its position relative to the viewport. Description – Denis Priebe Commented Mar 6, 2015 at 16:43
  • @BeastModeJoe hmm thanks! I tried document.getElementById("xzOwqphM4GGR_1").getBoundingClientRect() but I get undefined for everything. – bryan Commented Mar 6, 2015 at 17:12
 |  Show 4 more ments

4 Answers 4

Reset to default 1 +50

I am giving this a stab because I find these things interesting. It might just be a starting point since I have a headache today and am not thinking straight. I'd be willing to bet it can be cleaned up and simplified some.

I also might be over-plicating the approach I took, getting the first visible form, and the positioning. I didn't use the getBoundingClientRect function either.

Instead, I approached it trying to account for padding and margin, using a loop through parent objects up to the pageContent to get the offset relative to that element. Because the form is nested a couple levels deep inside the pageContent element you can't use position(). You also can't use offset() since that changes with scroll. The loop approach allowed me to factor the top margin/padding in. I haven't looked at the other solutions proposed fully so there might be a shorter way to acplish this.

Keeping in mind that the scale will affect the ACTUAL location of the child elements, you have to divide by your scale percentage when getting the actual location. To do that I moved the scalePercentage to a global var so it was usable by the zoom function and the click.

Here's the core of what I did. The actual fiddle has more logging and junk:

var visForm = getVisibleForm();
var formTop = visForm.position().top;

var parents = visForm.parentsUntil('#pageContent');
var truOffset = 0;
parents.each(function() {
    truOffset -= $(this).position().top;
});
// actual location of form relative to pageContent visible pane
var formLoc = truOffset - formTop;   
var scaledLoc = formLoc / scalePercent;

Updated Fiddle (forgot to account for scale in get func): http://jsfiddle/e6vpq9c8/5/

If I understand your question correctly, what you want is to catch when certain descendant elements reach the top of the outer container, and then determine the position of the visible "page" (div with class formContainer) relative to the top.

If so, the first task is to mark the specific elements that could trigger this:

<div class='triggerElement' style="position:absolute;top:50px;left:100px;width:450px;height:25px;background-color:#fff;color:#000;">When this reaches the top, the "trigger" should say 50px"</div>

Then the code:

// arbitrary horizontal offset - customize for where your trigger elements are placed horizontally
var X_OFFSET = 100;

// determine once, at page load, where outer container is on the page
var outerContainerRect;
$(document).ready(function() {
    outerContainerRect = $("#pageContent").get(0).getBoundingClientRect();
});

// when outer container is scrolled
$("#pageContent").scroll(function(e) {
    // determine which element is at the top
    var topElement = $(document.elementFromPoint(outerContainerRect.left+X_OFFSET, outerContainerRect.top));
    /*
    // if a trigger element
    if (topElement.hasClass("triggerElement")) {
        // get trigger element's position relative to page
        console.log(topElement.position().top);
    }
    */

    var page = topElement.closest(".formContainer");
    if (page.length > 0) {
        console.log(-page.get(0).getBoundingClientRect().top);
    }

});

EDIT: Changed code to check formContainer elements rather than descendant elements, as per your ment.

http://jsfiddle/j6ybgf58/23/

EDIT #2: A simpler approach, given that you know which formContainer to target:

$("#pageContent").scroll(function(e) {
    console.log($(this).scrollTop() - $("#xzOwqphM4GGR_1").position().top);
});

http://jsfiddle/rL4Ly3yy/5/

However, it still gives different results based on the size of the window. This seems unavoidable - the zoomProject and resize functions are explicitly resizing the content, so you would have to apply the inverse transforms to the number you get from this code if you want it in the original coordinate system.

I do not fully understand what it is that you are needing, but if i am correct this should do the trick

$("#pageContent").scroll(function(e) {
    // If more then 50 pixels from the top has been scrolled
    // * if you want it to only happen at 50px, just execute this once by removing the scroll listener on pageContent
    if((this.scrollHeight - this.scrollTop) < (this.scrollHeight - 50)) {
        alert('it is');
    }
});

ScrollHeight is the full height of the object including scrollable pixels. ScrollTop is the amount of pixels scrolled from the top.

You can use waypoints to detect the position of divs based on where you're scrolling.

Here is a link to their official website's example: http://imakewebthings./waypoints/shortcuts/inview/

发布评论

评论列表(0)

  1. 暂无评论