Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A browser JavaScript challenge #116

Closed
scripting opened this issue May 6, 2019 · 8 comments

Comments

Projects
None yet
4 participants
@scripting
Copy link
Owner

commented May 6, 2019

Here's a demo app.

Use the up and down arrow keys to move from card to card. When you get to the bottom of the screen the cursor moves to objects that are not visible. I want to scroll the window vertically to make the cursor object fully visible.

If you look at the code, the goal is to fill in the body of the scrollToMakeVisible routine.

@ouchiko

This comment has been minimized.

Copy link

commented May 6, 2019

function scrollToMakeVisible (idObjToMakeVisible) {
    var el = document.getElementById(idObjToMakeVisible);
    var t = el.offsetTop;
    var h = el.offsetHeight;
    var j = parseInt(window.scrollY) + parseInt(window.innerHeight);
    if (t<window.scrollY) {
        window.scrollTo(0,(window.scrollY-h));
    } else if ((t+h)>=j) {
        window.scrollTo(0,(window.scrollY+h));
    }
}
@scripting

This comment has been minimized.

Copy link
Owner Author

commented May 7, 2019

@ouchiko -- thanks for the solution. I've added it to the app.

@scripting scripting closed this May 7, 2019

@scripting

This comment has been minimized.

Copy link
Owner Author

commented May 7, 2019

Except it doesn't work as you hit the uparrow to move the cursor off the top of the screen. It only partially scrolls to make the item visible, and after a few scrolls it isn't making the item visible at all. I reopened the thread.

@scripting scripting reopened this May 7, 2019

@jmgs

This comment has been minimized.

Copy link

commented May 7, 2019

I build on what @ouchiko wrote with two more refined solutions that don't show the drifting: the second is generic, the first looks better but only works if all elements have the same height.

///////////////////////////////////////////////////////////////////
// This solution relies on all cards having the same height
// and the gap between cards being the same. It is more satisfying
// because cards don't "jump" when the screen scrolls.
///////////////////////////////////////////////////////////////////

idOfFirstCard = "img0";
idOfSecondCard = "img1";

function scrollToMakeVisible (idObjToMakeVisible) { 
	var card = document.getElementById(idObjToMakeVisible);
	var cardTop = card.offsetTop;
	var cardHeight = card.offsetHeight;
	var cardGap = getCardGap();
	var windowTop = window.scrollY;
	var windowHeight = window.innerHeight;	

	if (cardTop < windowTop - cardGap) {
		window.scrollTo(0, cardTop);
	} else if (cardTop < windowTop) {
		window.scrollTo(0, windowTop - cardGap);
	}
	
	if (cardTop + cardHeight > windowTop + windowHeight + cardGap) {
		window.scrollTo(0, cardTop + cardHeight - windowHeight);
	} else if (cardTop + cardHeight > windowTop + windowHeight) {
		window.scrollTo(0, windowTop + cardGap);
	}
}
	
function getCardGap() {
  var y0 = document.getElementById(idOfFirstCard);
  var y1 = document.getElementById(idOfSecondCard);
  return y1.offsetTop - y0.offsetTop;
}


///////////////////////////////////////////////////////////
// This solution works even if cards have different height
///////////////////////////////////////////////////////////

function scrollToMakeVisible (idObjToMakeVisible) { 
	var element = document.getElementById(idObjToMakeVisible);
	var elementTop = element.offsetTop;
	var elementHeight = element.offsetHeight;
	var windowTop = window.scrollY;
	var windowHeight = window.innerHeight;	

	if (elementTop < windowTop) {
		window.scrollTo(0, elementTop);
	}
	
	if (elementTop + elementHeight > windowTop + windowHeight) {
		window.scrollTo(0, elementTop + elementHeight - windowHeight);
	}
}
@ouchiko

This comment has been minimized.

Copy link

commented May 7, 2019

True, the height of the <img> tag is used and doesnt account for the margin which ends up being a floating point height value. Slightly refined with the actual height point adjusted. You could use the element.parentNode for height but I was looking for a more generic routine.

function scrollToMakeVisible (idObjToMakeVisible) {
	var el = document.getElementById(idObjToMakeVisible);
	var t = el.offsetTop;
	var h = 137.4; //el.offsetHeight;
    var j = parseInt(window.scrollY) + parseInt(window.innerHeight);
    if (t<window.scrollY) {
        window.scrollTo(0,(window.scrollY-h));
    } else if ((t+h)>=j) {
        window.scrollTo(0,(window.scrollY+h));
    }
}
@tommy-carlier

This comment has been minimized.

Copy link

commented May 7, 2019

DOM-elements have a method scrollIntoView that does this for you:

function scrollToMakeVisible(idObjToMakeVisible) {
  var el = document.getElementById(idObjToMakeVisible);
  el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

See documentation at https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView

@scripting

This comment has been minimized.

Copy link
Owner Author

commented May 7, 2019

@tommy-carlier -- that worked and it makes total sense that the browser, in 2019, would have this feature built-in. No need for every app to do it for itself. Thanks.

@scripting

This comment has been minimized.

Copy link
Owner Author

commented May 7, 2019

I also added the ability to click on a card to move the cursor to it. It was driving me nuts that it didn't work. Now the case is closed. This will be part of a new app I'm working on. Thanks to @ouchiko, @jmgs and @tommy-carlier for the generous help! :-)

@scripting scripting closed this May 7, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.