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>
Related
Is there a way to hide a whole div if part of it is empty? For example if "dd" is empty as shown below can I hide the whole class "test" so the keyword Restrictions does not show either. I tried .test dd:empty { display: none; } but this does not work. thanks!
<div class="test"><dt>Restrictions:</dt>
<dd></dd></div>
I don't think there's any easy way to do what you're talking about with just CSS. Better to test it server-side if you can. But if you can't here's some JS that will do the job.
<script type="text/javascript">
// handles multiple dt/dd pairs per div and hides them each conditionally
function hideIfEmpty() {
// get all the elements with class test
var els = document.getElementsByTagName('dl');
// for every 'test' div we find, go through and hide the appropriate elements
Array.prototype.map.call(els, function(el) {
var children = el.childNodes;
var ddEmpty = false;
for(var i = children.length - 1; i >= 0; i--) {
if(children[i].tagName === 'DD' && !children[i].innerHTML.trim()) {
ddEmpty = true;
} else if(children[i].tagName === 'DT') {
if(ddEmpty) {
children[i].style.display = 'none';
}
// reset the flag
ddEmpty = false;
}
}
});
}
window.addEventListener('load', hideIfEmpty);
</script>
<div class="test">
<div style="clear: both;"></div>
<dl>
<dt>Restrictions:</dt>
<dd></dd>
<dt>Other Restrictions:</dt>
<dd>Since I have content, I won't be hidden.</dd>
</dl>
</div>
Just a fair warning: the code uses some functions that may not exist in older IE, such as Array.prototype.map, String.prototype.trim, and addEventListener. There are polyfills available for these and you could also write your own pretty easily (or just do it with a for loop instead).
CSS alone can't do that. Either, you need a javascript to retrieve empty elements and hide their parents, or your CMS applies special CSS classes if there's no content.
Put as an answer as requested by #Barett.
You could update your CSS to be
.test{
display: none;
color: transparent;
}
This would make the text transparent too, but display:none should hide it anyway.
To make the div with the id test ONLY show when the dd tag is EMPTY, and you can use jQuery, try the following JavaScript along with the CSS:
if($("dd").html().length ==0)
{show();
}
Note: this solution requires jQuery, which is a JavaScript library.
Want to apply HTML 5 fullscreen APi to background image of div
<div class="bgimg" style="background-image:url('img/home-1.jpg')" />
<img src="img/fullscreen.png" id="fullscreen-btn">
</div>
I want onclick fullscreen-btn background image of div bgimg ie home-1.jpg should open in fullscreen. I tried below code but not workin Kindlt suggest
<scritpt>
$(function() {
var bg = $('.bgimg');
$('#fullscreen-btn').click(function () {
goFullScreen(bg.attr('style', 'background-image:url()'));
});
});
function goFullScreen( element )
{
if ( element === undefined )
{
// If no element defined, use entire document
element = document.documentElement;
}
if ( element.requestFullScreen )
{
// Spec, supported by Opera 12.1+
element.requestFullScreen();
}
else if ( element.mozRequestFullScreen )
{
// Supported by Firefox 10+
element.mozRequestFullScreen();
}
else if ( element.webkitRequestFullScreen )
{
// Supported by Chrome 15+ & Safari 5.1+
element.webkitRequestFullScreen();
}
// Still no IE support, sorry folks :(
}
Seems to be working for me. You just needed to add the image path in your javascript with quotes around it.
$(function() {
var bg = $('.bgimg');
$('#fullscreen-btn').click(function () {
goFullScreen(bg.attr('style', "background-image:url('img/home-1.jpg')"));
});
});
FIDDLE
I believe, but will admit am not 100% sure, that the fullscreen API can only full screen an HTML element. So that is why it will fullscreen div.bgimg but will not fullscreen the background image of the element. <img> is an HTML element, however, so I would think that would work. Is there any reason you would not want to use that instead of setting the background image of your divs?
If so, you could try to wire up some JS that connects visible divs with the background images (Like what you have now) to invisible images and load those to your fullscreen script instead.
I'm trying to make an image fit nicely on different screen sizes without breaking the layout. The following bit of CSS helps:
.viewer .main img {
max-width: 100%;
height: auto;
}
But the trouble is this image changes. I use a bit of Javascript to create a new img element each time the image changes, instead of reusing the existing one. (This seems a little more reliable for what I'm doing). The browser doesn't know the image's size until it is loaded, creating an obvious flicker in the interim. I deal with that by setting the image's width and height attributes in HTML. Without the above CSS rule, that works fine.
With that CSS, the flickering is still there. For some reason, when I create a new img element, the CSS seems to be causing the browser to ignore its width and height attributes, so. It ends up as ignorant of the aspect ratio as it was before.
Here's a jsfiddle to illustrate the situation:
http://jsfiddle.net/7sDtN/
One of the images in there is very very big (138 MB), so be careful if you're on a metered connection :)
What I would love is to get the image to scale according to those dimensions I set in HTML. Preferably in a nice way. A Javascript solution isn't the end of the world (I'm already using it, for course), but if there's an elegant CSS solution that would be very nice.
I ended up solving this in a roundabout way by wrapping the image in a dedicated container, along with some strange looking javascript to keep it in place as the image loads. The dimensions for that container are calculated as in Sven's answer, but ultimately it lets the browser take over. This way layout changes are kept fairly minimal and we end up only doing this crazy stuff for the bit of time between images.
Here's a big wad of code, for completedness:
function Viewer(container) {
var viewer = this;
container = $(container);
var pictureBox = $('.picture', container);
var img = $('<img>').appendTo(pictureBox);
var hide = function() {
/* [snip] */
}
var getPictureDisplayHeight = function(picture) {
var ratio = picture.data.h / picture.data.w;
var displayWidth = Math.min(pictureBox.width(), picture.data.w);
var displayHeight = Math.min(displayWidth * ratio, picture.data.h);
return displayHeight;
}
var stopLoadingTimeoutId = undefined;
var stopLoadingTimeout = function() {
container.removeClass('loading');
}
var showPicture = function(picture) {
var imgIsChanging = img.data('picture') != picture;
container.show();
/* This code expects to be cleaned up by stopLoadingTimeout or onImgLoaded, which will not fire if img src doesn't change */
if (imgIsChanging) {
container.addClass('loading');
window.clearTimeout(stopLoadingTimeoutId);
stopLoadingTimeoutId = window.setTimeout(stopLoadingTimeout, 3000);
}
pictureBox.css({
'min-height' : pictureBox.height()
});
var displayHeight = getPictureDisplayHeight(picture);
if (displayHeight > pictureBox.height()) {
/* Grow pictureBox if necessary */
pictureBox.stop(true, false);
pictureBox.animate({
'height' : displayHeight
}, 150);
}
/* I wish I could set width and height here, but it causes the current image to stretch */
img.attr({
'src' : picture.fullPath
}).data('picture', picture);
}
var onImgLoaded = function(event) {
/* The load event might not be fired, so nothing here should be essential */
var picture = img.data('picture');
container.removeClass('loading');
var displayHeight = getPictureDisplayHeight(picture);
pictureBox.stop(true, false);
pictureBox.animate({
'min-height' : 0,
'height' : displayHeight
}, 150, function() {
pictureBox.css('height', 'auto');
});
window.clearTimeout(stopLoadingTimeoutId);
}
var onImgClicked = function(event) {
selectNextPicture();
}
var onPictureSelectedCb = function(picture) {
if (picture) {
showPicture(picture);
} else {
hide();
}
}
var init = function() {
img.on('click', onImgClicked);
img.on('load', onImgLoaded);
}
init();
}
Relevant HTML:
<div class="viewer" style="display: none;">
<div class="picture"></div>
<div class="caption"><div class="caption-text"></div></div>
</div>
And CSS:
.viewer .picture img {
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
}
This way we leave space around the image that is either the size of the next image or the size of the current image, and never the smaller size that seems to happen before a new image is loaded (which kept happening for some reason). There are probably a million solutions to this, and mine doesn't feel especially straight-forward, so I'm certainly curious to see others :)
If I understand you right, you can achieve your goal by using the following code
HTML
<div id="Wrapper">
<img id="MyPic" src="http://www.davidstorey.tv/wp-content/uploads/2012/05/image.php_.jpeg" alt="" width="300" height="200" />
</div>
CSS
body{
width:100%;
}
#Wrapper{
width:98%;
border:1px solid red;
}
jQuery
$("document").ready(function(){
var ratio=$("#MyPic").width() / $("#MyPic").height();
$("#MyPic").css("width","100%");
$("#MyPic").css("height", $("#MyPic").width()/ratio+"px");
});
Here is the link to jsfiddle
I have two instances of where the Like Button is not "listening" to the width I specify in the XFBML code.
It LOOKS fine, but something is mysteriously causing the like button to be extra wide and force the browser to do horizontal page scrolling even though the entire like button is within the page.
Example:
[EXAMPLE REMOVED] - see the sidebar. I have to set the entire BODY to ignore overflow-x. If not, the like button causes a ton of extra pixels out there (but I can't see them with Firebug). I know this is the problem because if I remove it, then it looks fine.
Example 2:
Screenshot of Example - In the header, I had to move the margin over so far to the left, and I wanted it to be in the top-right corner. Play with the CSS for fbheader in firebug and you'll see.
The code I'm using there:
<div class="fbheader">
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({appId: 'your app id', status: true, cookie: true,
xfbml: true});
};
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
<fb:like href="http://www.example.com" send="true" width="300" show_faces="false" font="arial" colorscheme="dark"></fb:like>
</div> <!--// End fbheader -->
Any ideas why this is happening?? Can anyone help? It seems like a bug with the XFBML.
I had the same problem, but none of the suggestions above worked for me.
I found another solutions that did work, see http://britishinside.com/archive/2011/07/07/Facebook-Like-Button-Bug.aspx
Simply include this in your stylesheet:
#fb-root > div { left:0 }
It's a bug with facebook. Why don't you just update your fbheader class as follows:
.fbheader {
overflow:hidden;
}
That should solve your issue ..
Good luck..
I had the same problem. I found the problem was to do with a bug in Facebook's reset div. I fixed it like this:
#fb_like .fb_reset {
/* fix for Facebook bug which causes horizontal scrollbars in browser */
display: none;
}
I fixed the issue using #fb-root { display: none; }
You could try alter the width of fbheader class in css file, or even better, the parent element.
Sometimes when we use internal elements that cause the parent to get wider, the horizontal scroll get visible.
Another tip is to reposition the button, or set the margins and padding narrow.
My solution is to apply this to the parent container:
.my-parent-wrapper {
display: inline-block;
overflow: hidden;
}
skipping overflow rule will work too
I've just had the same problem, using an iFrame script from facebook for a double "share" and "like" button. I hadn't specified a width in pixels. Fixed it by getting a replacement script, but this type specifying a width of 120 pixels in the box provided.
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.