0

I'm using this plugin to create a horizontal timeline. You can use the left and right arrows to navigate between dates. Clicking on a date will change the footer to the one corresponding to that same date.

My problem is the following: I am trying to know if certain dates are visible in the current viewport. So for instances when the plugin is first loaded the following dates: "16 Jan", "28 Feb", "20 Mar", "20 May" are shown. When I click on the right arrow these dates are replaced by the following dates: "09 Jul", "30 Aug", "15 Sep", "01 Nov". Now, at this point, I would like to know, for example, if the date "16 Jan" is visible or not (In this case it is not visible).

Since each date is added to the a element that corresponds to each date in the form of a data-date attribute I can get the element that I want to check with the following selector (example for "16 Jan"):

$("a[data-date='16/01/2014'");

Now in order to check the visibility I have tried two different approaches:

First Approach (as seen in this answer)

$("a[data-date='16/01/2014'").is(':visible');

This approach always returns me true and thus does not work.

Second Approach (as seen in this answer)

function isElementInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

And afterwards:

var a = $("a[data-date='16/01/2014'");
var b = isElementInViewport();

This approach always returns me true and thus also does not work.

I can understand that these approaches might not work because the way the elements are showing and disappearing after each arrow click is due to the use of the CSS function scaleX(), whose impact I have no idea in this specific situation.

So after all of this my question is the following: Is it even possible to check the visibility of the elements in this specific case? And if so, how can I do it?

Community
  • 1
  • 1
João Paiva
  • 1,937
  • 3
  • 19
  • 41
  • Can you just add a variable flag on click that say `isDateOpen = true`? or something like that? Since you already have some functionality around it. – ntgCleaner Jan 16 '17 at 18:49
  • @ntgCleaner I could but in order to do that I would have to calculate the width that is shown per page, the total width of the timeline, calculate the number of pages, make a data structure that associates the dates to the corresponding page and then when I click on the arrow add a property to the dates like you suggested. This however is my last resort. I was looking for something simpler. – João Paiva Jan 16 '17 at 18:54

2 Answers2

1

The First Approach doesn't work because the elements are actually 'visible'. The following line checks simply the value of the css visibility attribute which is 'visible' (the default value) for each of the date elements.

$("a[data-date='16/01/2014'").is(':visible');

However, you still can't see some of the date elements. This is because these hidden elements actually lie outside their container. The Second Approach heads you towards one of the possible solutions. The following function takes an element and determines whether it is in the view port.

function isElementInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
         el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight ||    document.documentElement.clientHeight) && /*or $(window).height() */
        rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
    );
}

Note that in the above code the view port is assumed to be window. This will be useful if you want to find out if an element is outside the screen area. But your requirement is slightly different. In your example, the view port should be the container of these date elements. If you inspect the timeline on your browser you will find a div with the class event-wrapper outside which all the dates are hidden. So, you can modify the above function as follows:

function isElementReallyInViewport (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
         el = el[0];
    }

    var rect = el.getBoundingClientRect();
    var container = $('.events-wrapper')[0].getBoundingClientRect();

    return (
        rect.top >= container.top &&
        rect.left >= container.left &&
        rect.bottom <= container.bottom &&
        rect.right <= container.right
    );
}

I have tested it on a couple of elements in the example that you had shared and it seems to work fine. I hope it helps you.

rohit-biswas
  • 815
  • 1
  • 11
  • 23
0

One way would be to compare the horizontal positions:

function isElementVisible($elOrDate) {

    var $el = (typeof $elOrDate === "string") ? $("a[data-date='" + $elOrDate + "'") : $elOrDate;

    // We need the .event-wrapper element of the timeline
    let $eventWrapper = $el.closest('.event-wrapper');

    // Get the position of the timeline:
    var timelinePosition = $eventWrapper[0].getBoundingClientRect();

    // Then get the position of the element you're interested in:    
    var targetPosition = $el.getBoundingClientRect();

    var targetIsNotOffToTheLeft  = targetPosition.right > timelinePosition.left;
    var targetIsNotOffToTheRight = targetPosition.left < timelinePosition.right;

    // The timeline is not scrollable vertically, so we only need to worry
    // about the horizontal position
    return targetIsNotOffToTheLeft && targetIsNotOffToTheRight;

}

// Usage:

console.log(isElementVisible($("a[data-date='16/01/2014'")));

// Or with just the date:

console.log(isElementVisible("16/01/2014"));
Schlaus
  • 18,144
  • 10
  • 36
  • 64