This html file shows a 3 panel layout. A css transition on the "left" property gives me a nice sliding effect when I change panels. It all works but, when I change panels, I want to place the focus on an input in the new panel. Can anyone tell me why the call to focus() breaks the css transition, and lands me half way between two panels? It only happens going forward. It works if I comment out the transition, and it works if I delay the call to focus() with a timeout.
The problem occurs in Chrome, IE and Firefox.
var panelContainer;
var panels = new Array();
var numPanels;
var currentPanel = 0;
window.addEventListener('load', init, false);
function init() {
setTimeout(function() {
window.scrollTo(0, 1);
}, 10); //hiding the browser address bar in iPhone/Android
panelContainer = document.getElementById("panelContainer");
panels = document.getElementsByClassName("panel");
numPanels = panels.length;
sizeResize();
}
function sizeResize() {
panelContainer.style.width = (numPanels * window.innerWidth) + "px";
panelContainer.style.height = window.innerHeight + "px";
for (var i = 0; i < numPanels; i++) {
panels[i].style.width = window.innerWidth + "px";
panels[i].style.height = window.innerHeight + "px";
}
slide();
}
window.onresize = sizeResize;
function slide(panel) {
if (panel != undefined)
currentPanel = panel;
panelContainer.style.left = -(window.innerWidth * currentPanel) + "px";
if (panel >= 0) {
panels[panel].getElementsByTagName("input")[0].focus(); //shows problem
//window.setTimeout(function () { panels[panel].getElementsByTagName("input")[0].focus() }, 100);//no problem
}
}
#wrapper {
width: 100%;
height: auto;
overflow: hidden;
}
#panelContainer {
position: relative;
transition: left 1000ms ease;
}
.panel {
float: left;
}
<div id="wrapper">
<div id="panelContainer">
<div id="panel0" class="panel" style="background-color: #888;">
panel zero
<input id="btn0" class="focussable" type="button" onclick="slide(1);" value="forward" />
</div>
<div id="panel1" class="panel" style="background-color: #AAA;">
panel 1
<input id="btn1" class="focussable" type="button" onclick="slide(2);" value="forward" />
<input type="button" onclick="slide(0);" value="back" />
</div>
<div id="panel2" class="panel" style="background-color: #CCC;">
panel 2
<input id="btn2" class="focussable" type="button" onclick="slide(1);" value="back" />
</div>
</div>
</div>
This html file shows a 3 panel layout. A css transition on the "left" property gives me a nice sliding effect when I change panels. It all works but, when I change panels, I want to place the focus on an input in the new panel. Can anyone tell me why the call to focus() breaks the css transition, and lands me half way between two panels? It only happens going forward. It works if I comment out the transition, and it works if I delay the call to focus() with a timeout.
The problem occurs in Chrome, IE and Firefox.
var panelContainer;
var panels = new Array();
var numPanels;
var currentPanel = 0;
window.addEventListener('load', init, false);
function init() {
setTimeout(function() {
window.scrollTo(0, 1);
}, 10); //hiding the browser address bar in iPhone/Android
panelContainer = document.getElementById("panelContainer");
panels = document.getElementsByClassName("panel");
numPanels = panels.length;
sizeResize();
}
function sizeResize() {
panelContainer.style.width = (numPanels * window.innerWidth) + "px";
panelContainer.style.height = window.innerHeight + "px";
for (var i = 0; i < numPanels; i++) {
panels[i].style.width = window.innerWidth + "px";
panels[i].style.height = window.innerHeight + "px";
}
slide();
}
window.onresize = sizeResize;
function slide(panel) {
if (panel != undefined)
currentPanel = panel;
panelContainer.style.left = -(window.innerWidth * currentPanel) + "px";
if (panel >= 0) {
panels[panel].getElementsByTagName("input")[0].focus(); //shows problem
//window.setTimeout(function () { panels[panel].getElementsByTagName("input")[0].focus() }, 100);//no problem
}
}
#wrapper {
width: 100%;
height: auto;
overflow: hidden;
}
#panelContainer {
position: relative;
transition: left 1000ms ease;
}
.panel {
float: left;
}
<div id="wrapper">
<div id="panelContainer">
<div id="panel0" class="panel" style="background-color: #888;">
panel zero
<input id="btn0" class="focussable" type="button" onclick="slide(1);" value="forward" />
</div>
<div id="panel1" class="panel" style="background-color: #AAA;">
panel 1
<input id="btn1" class="focussable" type="button" onclick="slide(2);" value="forward" />
<input type="button" onclick="slide(0);" value="back" />
</div>
<div id="panel2" class="panel" style="background-color: #CCC;">
panel 2
<input id="btn2" class="focussable" type="button" onclick="slide(1);" value="back" />
</div>
</div>
</div>
Share
Improve this question
edited May 20, 2021 at 7:13
vsync
130k59 gold badges340 silver badges421 bronze badges
asked Nov 6, 2014 at 15:21
bbsimonbbbbsimonbb
29k18 gold badges90 silver badges126 bronze badges
6
- Can you provide a live demo for us to edit? – Zach Saucier Commented Nov 6, 2014 at 15:56
- For whatever reason, my attempt to reproduce this as a jsfiddle didn't work. You can save the code locally, and open it in a browser from your file system. I don't have a web facing server handy sorry. – bbsimonbb Commented Nov 6, 2014 at 16:22
- 1 This is not a total answer, but maybe useful... The browsers have rules for scrolling the active element into view. These rules have precedence over whatever behaviour you might try to script. In the above example, tabbing also caused me problems. The workaround for the moment is to manage focus at all times. Something on the page must always be focussed, and calls to focus() are made inside setTimeout() so transitions have a chance to complete. – bbsimonbb Commented Dec 5, 2014 at 8:38
- Here's a working jsFiddle showing the issue. – Brett DeWoody Commented Feb 17, 2015 at 23:38
- Thank you that shows the issue perfectly. I've changed the timeout to 300ms, and now the timeout version shows the desired behaviour. Tis a tricky one. – bbsimonbb Commented Feb 19, 2015 at 9:51
2 Answers
Reset to default 17As @user1585345 correctly stated a browser immediately scrolls to the focused element in order to bring it inside the current view, and not a px further!
What happens here is the following: you change the relative position of your element, but immediately you change focus, since the focused elements when the event fires is not visible the content also scrolls (ending the animation abruptly).
You should explicitly fire your focus only when the animation is complete, so that the browser recognise that no scrolling is necessary.
One solution is (suggested by @Mark)
there are two event listeners 'ontransitionend' and 'onanimationend' you can add an event for and then fire your focus() function
As mentioned in another answer, focus()
scrolls to the element and visually breaks any transition mid-flow.
If you didn't want to rely on listeners, and depending on your requirements, you could use the preventScroll: true
flag when calling the focus()
method.
input.focus({ preventScroll: true })
See the MDN documentation here and also check browser compatibility. Not all browsers support this but many of them do.