Problem with scrolling in opera - css

Update: I left the following javascript code in to show how the problem developed over time, but it turns out now this is NOT relevant as javascript is not the issue. Please take a look at the html/css code below.
For an overview page with a large menu, I implemented the following function (most of which I stole from here):
function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
alert(elemBottom);
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom));
}
$(document).ready(function(){
var overview = $('#overview');
var active = $('#active-item');
if (!isScrolledIntoView(active))
{
$('#overview li.active-parent').each(function(index, value){
if (!isScrolledIntoView(active)) overview.scrollTo(value);
});
}
if (!isScrolledIntoView(active)) overview.scrollTo(active);
});
The idea is that after every page load, the containing the menu is scrolled into a position where the current #active-item is visible. Preferably by scrolling to its first parent (the menu items are in a tree) otherwise to the item itself.
Now this works fine in Firefox and Chrome (and none of the apple people have complained to me), but Opera does a really strange thing: it scrolls down to the correct element, then pauses very briefly before scrolling all the way up again
Does anybody have any idea
What's going on, and
How can I stop it?
Thanks,
Update: I'm testing with version 10.63 on linux (Fedora)
Update: it appears I was searching in the wrong direction entirely. The issue appears to be a css thing, and can be replicated with the following code:
<html>
<head>
<title>Opera scroll test</title>
<style>
.main:after
{
content: 'abc';
}
:focus
{
padding: 0px;
}
#overview
{
display: block;
float: left;
width: 219px;
height: 500px;
overflow: auto;
}
</style>
</head>
<body>
<div id="main" class="main">
<div id="overview">
<ul>
<?
for($i = 1; $i < 100; $i++)
echo '<li>'.$i.'</li>';
?>
</ul>
</div>
<div>
<p>123</p>
</div>
</div>
</body>
</html>
Now if you scroll down on the navigation pane, and the move your mouse to the right (over the content pane) the scroll of the navigation pane is reset.
Sorry for wasting everyone's time with a javascript hunt :(
If there are any css gurus out there who know how to fix it, or who can simply explain what's going on I'd be very grateful.
Update: tested the above code in windows on opera 10.63. The same strange behaviour occurs.
Update: have reported this to Opera as a bug. Wonder what will happen...

Not sure as to the why but the issue seems directly related to the .main:after content CSS setting. Can you use jQuery? If so, if you comment out or remove the .main:after CSS setting and replace it with the following script in your <head> tag you get the same visual result without the weird scrolling issue:
<script type="text/javascript">
$(document).ready(function(){
$(".main").append("abc");
});
</script>

I have an idea of whats going on, however I tried your code and so far I cant seem to replicate the problem. Chrome and Opera behave the same.
This is the HTML I tried with:
<div id="overview" style="height: 300px;overflow-y: scroll;">
<ul>
<li class="item" style="height: 400px;">item 1</li>
<li class="item" style="height: 400px;">item 2 </li>
<li class="active-parent" style="height: 400px;">active-parent<br/><br/>
<ul>
<li id="active-item" style="height: 300px;">active-item</li>
</ul>
</li>
</ul>
</div>
As to why it doesnt work in Opera: which version of Opera are you using? I tried 10.63 (the latest) and its fine on that one.
Since I cant reproduce the issue I'm going to take a blind shot at it:
Try introducing a slight delay so this code gets executed after everything else in the queue:
$(document).ready(function() {
window.setTimeout(function() {
// Your code goes here
}, 500);
});
LATER ADDITION:
In response to your statement: "I really would like to understand what's going on here though. Why a delay? And would the delay need to be longer on slower computers?":
If setting a delay fixes your problem then the reason is clear, there is some other piece of code interfering with the "scroll to top" functionality that was executing after it. By setting a delay of 500ms you are making sure your code runs after every other piece of Javascript that executes when a document loads, this could could either be something you explicitly added to the jQuery function queue when the document loads (using $(document).ready(function(){), or implicitly added by Opera in the form of a "Widget" (do you have any translation tools, google toolbars etc, installed?).
As to whether the delay needs to be longer for slower computers, I personally dont think so, the key is that the delay forces your code to execute AFTER EVERY OTHER SCRIPT triggered by the document.onload event. Since my suspicion is that whatever is intefering executes in direct succession after the document loads you don't really need a very long delay, even 50ms might do it, the key is that the delay forces your code to go at the back of the queue.
Hope this helps make things more clear.

As Steven de Salas mentioned, adding a delay helps -> $(window).scrollTop() returns 0 if not delayed!!
I played around a bit and came up with this example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Scrolling Test</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js" type="text/javascript"></script>
<style type="text/css">
.parent { margin-bottom:100px; border:1px solid red; }
</style>
<script type="text/javascript">
(function ($){
$.fn.extend({
viewport : function(){
if (this.length > 0) {
var element = $(this.get(0)),
pad = $.fn.viewport.PADDING,
vp = {
height : element.height() + 2 * pad,
top : element.offset().top - pad,
docHeight : $(window).height(),
docTop : $(window).scrollTop(),
completelyInViewport : false
};
vp.bottom = vp.top + vp.height + pad;
vp.docBottom = vp.docTop + vp.docHeight;
vp.fitsInViewport = vp.height <= vp.docHeight;
if (vp.top > vp.docTop && vp.bottom < vp.docBottom) {
vp.completelyInViewport = true;
}
return vp;
}
return null;
}
});
$.fn.extend($.fn.viewport, {
PADDING: 10, // ADJUST TO YOUR NEEDS
LARGE_PARENT_BEHAVIOR: "bottom" // if parent list is bigger than viewport and
// the active item is not in the first page of
// the parents list, where should it be shown
// possible: "bottom", "middle" or "top"
});
$.extend({
ensureViewport: function(element, parent) {
var e_vp = element.viewport(),
p_vp = parent.viewport();
if (null == e_vp || null == p_vp) {
return;
}
if (!p_vp.completelyInViewport) {
if (p_vp.fitsInViewport || e_vp.bottom - p_vp.top <= e_vp.docHeight) {
doScroll(p_vp.top);
} else {
switch($.fn.viewport.LARGE_PARENT_BEHAVIOR) {
case "top":
doScroll(e_vp.top);
break;
case "middle":
doScroll(e_vp.top - (e_vp.docHeight - e_vp.height)/2);
break;
case "bottom":
default:
doScroll(e_vp.bottom - e_vp.docHeight);
break;
}
}
}
function doScroll(y){
window.scrollTo(0, y);
// you could implement instead some sort of smooth scroling mechanism here
// e.g. http://github.com/kswedberg/jquery-smooth-scroll
}
}
});
$(function(){
window.setTimeout(function(){
var item = $("li.active-item");
if (item.size() > 0) {
$.ensureViewport(item, item.closest("li.parent"));
}
}, 0);
});
})(jQuery);
</script>
</head>
<body>
<div id="overview">
<ul>
<li class="parent">
parent 1
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
<li class="parent">
parent 2
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
<li class="parent">
parent 3
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
<li class="parent">
parent 4
<ul class="item"><li>item 1</li><li class="active-item">item 2</li></ul>
</li>
<li class="parent">
parent 5
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
<li class="parent">
parent 6
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
<li class="parent">
parent 7
<ul class="item"><li>item 1</li><li>item 2</li></ul>
</li>
</ul>
</div>
</body>
</html>
Tested it on Opera 10.63 and it's working there too.
Cheers

This is just a theory, but might get you started.
When you press back on a browser, it often takes you to the point in the page where you were last viewing.
Is it possible that opera is trying to take you to that point, which is interfering with your code?
I wonder if you clear your history / cache, kill the opera process and start with a new process. Does that make any difference.

Related

Vertical scrolling in iOS not smooth

I've been searching far and wide through documentation regarding -webkit-overflow-scrolling: touch;, but I can only get it to work partially for my <body> element this way...
<body style="-webkit-overflow-scrolling: touch;">
or like this...
<body style="overflow-y: auto; -webkit-overflow-scrolling: touch;">
In iOS, my page will scroll with momentum about a fourth of the way down in the page, but then stop. So -webkit-overflow-scrolling: touch does work for a "part" of the body
Without this code, it will scroll all of the way through the page, but with no momentum and with a lot of jerky motion.
What about applying the -webkit-overflow-scrolling: touch; to all elements of your site:
* {
-webkit-overflow-scrolling: touch;
}
And you should create an extra CSS file instead of using the css attribute.
overflow should be 'scroll'
ref: https://css-tricks.com/snippets/css/momentum-scrolling-on-ios-overflow-elements/
I'm using WKWebView on an iPhone, iOS 12. I got no help with -webkit-overflow-scrolling:touch; But, I was able to implement a smooth scroll using a WKUIDelegate method for intercepting alert() calls. Instead of performing the alert(), I set the scrollView's contentOffset to a position value that's sent via the alert().
// in HtmlTable_VC.m
- (void)viewDidLoad
{
[super viewDidLoad];
wKWebView.scrollView.decelerationRate = UIScrollViewDecelerationRateFast;
// go figure -- faster deceleration seems to slow the scrolling rate
wKWebView.UIDelegate = self; // WKUIDelegate
// ...
NSString *htmlText = #"<body>Your HTML page text here</body>";
[wKWebView loadHTMLString:htmlText baseURL:[NSBundle mainBundle].bundleURL];
}
// WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
// if the message is numeric, smooth scroll the wkWebView
CGPoint scrollPoint = CGPointMake(0, [message intValue]);
[self->wKWebView.scrollView setContentOffset:scrollPoint animated:YES];
completionHandler();
// if not numeric, it was a real alert() interception, can process here
}
And the HTML file (Help.html):
<head>
<script>
function smoothScrollTo( anchor ) {
var el = document.getElementById(anchor);
// loop up through the element's parents and combine their offsets
var elTopPos = 0;
while ( el != null ) {
elTopPos += el.offsetTop;
el = el.offsetParent;
}
alert(elTopPos); // send to HtmlTable_VC: runJavaScriptAlertPanelWithMessage
// which will do the smooth scroll
}
</script>
</head>
<body>
Your HTML here
<div id="id1"> Stuff1 </div>
<div id="id2"> Stuff2 </div>
...
<a onclick="smoothScrollTo('id1')" href="">Go to Stuff1</a>
<a onclick="smoothScrollTo('id2')" href="">Go to Stuff2</a>
...
</body>

CSS styling via data attribute is not working properly on IE

I have a list with data attribute "data-layout", it can get two options "vertical" and "horizontal".
In my CSS i change the list items display property according to the layout.
On chrome it works as expected but on IE (tested on IE11) it does not redraw the screen with the change.
If i enter IE's devtools and select on of the items in the elements panel then only it redraws to the correct state.
Here is a reproduction of the problem.
http://fiddle.jshell.net/dimshik/bss3je3u/
Thank you.
document.getElementById('toggle').addEventListener('click',function(){
var list = document.getElementById('list');
if(list.dataset.layout == 'vertical'){
list.dataset.layout = 'horizontal';
} else {
list.dataset.layout = 'vertical';
}
});
[data-layout="horizontal"] li {
display: inline;
padding: 0 10px;
}
<ul id="list" data-layout="vertical">
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
</ul>
<br>
<br>
<button id="toggle">Toggle Layout</button>
It seems dataset is changing, but not re-rendering the css.
I've changed your code to setAttribute and it worked (IE11);
http://fiddle.jshell.net/bss3je3u/2/
For some reason IE11 doesn't repaint dom when HTMLElement.dataset is used in Javascript
For Example the below snippet doesn't work on IE11 when you click the button - box color doesn't change from red to blue as expected.
To fix this use test.setAttribute('data-action','style'); instead in the button event listener.
var button = document.getElementById('check');
var test = document.getElementById('test');
button.addEventListener('click', function(evt) {
test.setAttribute('data-action', 'style');
});
#test {
width: 200px;
height: 200px;
background-color: red;
}
#test[data-action="style"] {
background-color: blue;
}
<div id="test"></div>
<button id="check">Click Me</button>
This seems like IE isn't seeing that it needs to redraw.
Here's a slightly inelegant approach, but it illustrates the issue.
Adding a function to hide then immediately show to element that should change forces a redraw and the update appears to work.
I'm not sure you'll be able to take this exact thing forward - you may need to choose a different way of forcing a redraw or it may work out that the styles you eventually apply force a repaint so you can lose this altogether, but it does at least perform the switch now.
document.getElementById('toggle').addEventListener('click',function(){
var list = document.getElementById('list');
if(list.dataset.layout == 'vertical'){
list.dataset.layout = 'horizontal';
} else {
list.dataset.layout = 'vertical';
}
list.style.display = "none"
list.style.display = "block"
});

Why does riot.js break css?

In order to learn riot.js I started from well-known bootstrap navbar example. Then I added my custom tag using riot.js:
<script type="riot/tag">
<menu-item>
<li><a href={this.href}><yield/></a></li>
this.href = opts.href
</menu-item>
</script>
<script src="https://cdn.jsdelivr.net/g/riot#2.2(riot.min.js+compiler.min.js)"></script>
<script>
riot.mount('*')
</script>
Finally I tried to use my new tag, replacing
<li>JavaScript</li>
by
<menu-item href="http://getbootstrap.com/javascript">JavaScript</menu-item>
Result is broken. Why? (original non-broken example can be found here: jsfiddle.net/0hp9pwpu)
Your riot tag markup is inserted into your riot tag i.e. what happens is
ul
li
from your working example is actually
ul
menu-item
li
in your non-working example. Since bootstrap styles the navigation items expecting a certain hierarchy, your result is broken.
This was raised as an issue (https://github.com/riot/riot/issues/295) and closed using https://github.com/riot/riot/pull/569 i.e. instead of using the riot tags directly there is an option to add the riot tag as an attribute. So something like
<li riot-tag="menu-item" href="http://getbootstrap.com/javascript">JavaScript</li>
Admittedly, it is not as semantic
Fiddle - http://jsfiddle.net/86khqhwu/
Bootstrap is not adapted for use with Riot.js
Your resulted html is :
<menu-item href="http://getbootstrap.com/javascript">
<li>
JavaScript
</li>
</menu-item>
Bootstrap css is broken ...
Perhaps not so elegant, but in riot 2.3.13 I'm using something like this in a .tag file:
<menu-bar>
<ul list="<yield/>">
<li each={ item in items }>
{ titles[item] }
</li>
</ul>
<script>
this.titles = {
inventario: 'Inventario',
resguardos: 'Resguardos',
catalogos: 'Catálogos',
reportes: 'Reportes',
configurar: 'Configurar',
utilidades: 'Utilidades'
}
this.items = null
this.on('mount', function () {
var el = this.root.querySelector('ul')
this.items = el.getAttribute('list').trim().split(/,\s?/)
el.removeAttribute('list')
this.update()
})
</script>
</menu-bar>
Now, in the HTML page:
<menu-bar>
inventario,resguardos,catalogos,reportes
</menu-bar>
Works.

AngularJs translation directive stop working when I change div class using jquery

Well, being straight forward the problem is my $scope.$apply() is also not digesting the changes to rerun the translate directive.
I show you the HTML with applied translation directive and jQUERY code to change the class on resize of the windows (client).
HTML for menu:
<div id="navigation" ng-cloak>
<div class="container-fluid">
BRAND NAME
<ul class='main-nav'>
<li ng-class="{'active':activeLink == 'home'}">
<a href="#/">
<span>Home</span>
</a>
</li>
<li ng-class="{'active':activeLink == 'planning'}" data-trigger="hover">
<a href="#" data-toggle="dropdown" class='dropdown-toggle'>
<span>Planning</span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<a href="#" data-toggle="dropdown" class='dropdown-toggle'>Goals</a>
<ul class="dropdown-menu">
<li>
{{'TOP_MENU.GOAL_LIST' | translate}}
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
HERE is the JQuery Code to make it responsive for screen size changes.
function checkLeftNav() {
var $w = $(window),
$content = $("#content"),
$left = $("#left");
if ($w.width() <= 840) {
if (!$left.hasClass("mobile-show")) {
$left.hide();
$("#main").css("margin-left", 0);
}
if ($(".toggle-mobile").length == 0) {
$("#navigation .user").after('<i class="fa fa-bars"></i>');
}
if ($(".mobile-nav").length == 0) {
createSubNav();
}
} else {
if (!$left.is(":visible") && !$left.hasClass("forced-hide") && !$("#content").hasClass("nav-hidden")) {
$left.show();
$("#main").css("margin-left", $left.width());
}
$(".toggle-mobile").remove();
$(".mobile-nav").removeClass("open");
if ($content.hasClass("forced-fixed")) {
$content.removeClass("nav-fixed");
$("#navigation").removeClass("navbar-fixed-top");
}
if ($w.width() < 1200) {
if ($("#navigation .container").length > 0) {
versionFluid();
$('body').addClass("forced-fluid");
}
} else {
if ($('body').hasClass("forced-fluid")) {
versionFixed();
}
}
}
}
Now What my solution was to get he nav element scope on which the jquery is applying class and call the $apply() on its scope. which is not working.
Solution Code:
$(window).resize(function(e) {
checkLeftNav();
// get the scope of element and apply $apply()
var sc = angular.element('.mobile-nav').scope();
sc.$apply();
});
But still when ever the screen size is changed to mobile view less than 840 I can see direct code values instead of translated text in menu like this. and When I change back to screen width more than 840 it shows correct translated text. I am experimenting this on chrome on my pc by resizing. I checked on mobile its not translating there too.
AngularJS provides two-way data binding, not two way everything binding. It's not intended to be used this way. $apply() only looks at the data model - it is the function you call when you want to say "Hey, Angular, I've updated the data model, come have a look!" That is literally its only purpose. There is no method in Angular designed to look at the DOM itself for changes - that's very inefficient and against Angular's philosophies anyway, which is why it ships without jQuery.
You might want to evaluate other frameworks that better support this kind of thing. However, if you really wish to do this you can easily convert it into a proper AngularJS module. The best way is to simply paste all of this code into a controller, and then change the window resize binding to use Angular's $window service, like:
var windowElement = angular.element($window);
windowElement.bind('resize', function() {
// Do my calculations here.
});
With your calculations would you compute the same variables but you would store them in scope variables and then adjust your template to use them. For example, what you're doing with $(".toggle-mobile").remove(); could be replaced by:
if (windowElement.width > 840) {
// Other code here
$scope.isMobile = false;
} else {
$scope.isMobile = false;
}
and in your template:
<i class="fa fa-bars"></i>
Give it a whirl, play with it for a week or two, and you'll never go back to jQuery. It takes a lot of getting used to, but once you do you realize how broken the whole "I'm looking at my template and have no idea what mystery event handlers are bound to all this stuff" concept was to begin with.

Images not displaying when Print Preview (Or Print) in IE/Chrome/Firefox

I'm Web Developer and almost never work with design but have been given this bug which I'm struggling to rectify.
Some images appear correctly when I print/display the print preview page, however others don't. The key difference that I can see is that the images that don't appear are span tags with the image applied in css whilst the working images use the img tag.
Here are examples of the html:
Span with "icon" birth does not display:
<li class="media">
<div class="img">
<div class="h2 mtm">1889</div>
<span class="timeline-icon icon-birth"></span>
</div>
<div class="bd">
<h3 class="profile-subtitle mts">Born in ?</h3>
<p class="deemphasis mbn">
Search for Birth Record
</p>
</div>
</li>
Image.gif does display:
<li class="media">
<div class="img">
<div class="h6">
<strong>Spouse</strong></div>
<img src="image.gif" alt="" class="person-thumb dropshadow" />
</div>
<div class="bd">
<p class="mbn">Husband</p>
<h3 class="profile-subtitle">
Thomas <strong>Name</strong>
</h3>
<p class="mbn">?-?</p>
</div>
</li>
In some browsers it looks ok in the preview but does not print, in others it doesn't and still does not print.
Thankyou in advance!
I had the same problem over two months ago. I had a button that redirected users to a printer-friendly page and then I triggered print dialog using Javascript.
The problem was that browsers did not wait till images specified in CSS background were downloaded.
I put timeout before triggering the print dialog to give browser time to download images. This approach is not reliable since nobody has constant download speed and it would open the print dialog before the images are downloaded to users with very slow Internet connection.
In the end, I used HTML img tags to embed images on my page and jQuery to trigger the print dialog when the last image is downloaded.
You need to put delay before print. There is a native bug in chrome. Code would as under :-
function PrintDiv(data) {
var mywindow = window.open();
var is_chrome = Boolean(mywindow.chrome);
mywindow.document.write(data);
if (is_chrome) {
setTimeout(function() { // wait until all resources loaded
mywindow.document.close(); // necessary for IE >= 10
mywindow.focus(); // necessary for IE >= 10
mywindow.print(); // change window to winPrint
mywindow.close(); // change window to winPrint
}, 250);
} else {
mywindow.document.close(); // necessary for IE >= 10
mywindow.focus(); // necessary for IE >= 10
mywindow.print();
mywindow.close();
}
return true;
}
Just add the below styles to make it work img{max-width:100%;height:auto;}
Its the width of the image being set to 0 in the print which is causing the issue.
make a print stylesheet, set your span to display:block

Resources