media query for vertical scroll - css

Is there a way to detect vertical scroll distance with a media query?
It seems that media queries are designed around detecting the medium (shocking right :P) so things like browser height are testable, but not specifically how far down the page is scrolled.
If is not possible, but you know a way in JS (not jQuery) feel free to post!

First off, the accepted answer doesn't work.
The correct name is
window.onscroll
and not
window.onScroll
https://developer.mozilla.org/en-US/docs/Web/API/window.onscroll
Second, this is horribly inefficient as the function is called way more than it needs to and can make the page laggy when scrolled. From John Resig:
http://ejohn.org/blog/learning-from-twitter/
Much better to use a timer that runs every 150 ms or so - something like:
var scrolled = false;
window.onscroll = function() {
scrolled = true;
}
setInterval(function(){
if (scrolled) {
scrolled = false;
// scrollFunction()
}
}, 150);

I don't believe it's possible with a CSS media query, but I do know that the scroll height can be found in JavaScript using window.pageYOffset. If you wanted to run this value through a function every time the users scrolled up or down on a page, you could do something like
window.onscroll = function() {
scrollFunctionHere(window.pageYOffset);
};
Or just:
window.onscroll = scrollFunctionHere;
If the function itself checked the value of window.pageYOffset.
For more advice on how to do use window.onscroll efficiently in JavaScript, refer to mynameistechno's answer.
Important note on efficiency: running a function every single time a scroll event is emitted can tear through CPU cycles if anything non-trivial is performed in the callback. Instead, it is good practice to only allow a callback to run so many times per second. This has been termed "debouncing".
Simple debounced scroll event handler code below. Notice how the text toggles between "HELLO" and "WORLD" every 250ms, rather than every single frame:
var outputTo = document.querySelector(".output");
var timeout_debounce;
window.addEventListener("scroll", debounce);
function debounce(event) {
if(timeout_debounce) {
return;
}
timeout_debounce = setTimeout(clearDebounce, 250);
// Pass the event to the actual callback.
actualCallback(event);
}
function clearDebounce() {
timeout_debounce = null;
}
function actualCallback(event) {
// Perform your logic here with no CPU hogging.
outputTo.innerText = outputTo.innerText === "HELLO"
? "WORLD"
: "HELLO";
}
p {
padding: 40vh;
margin: 20vh;
background: blue;
color: white;
}
<p class="output">Test!</p>

In Jquery you have the method .scrollTop()
http://api.jquery.com/scrolltop/
This example make a div scroll with the window scroll.
$(window).scroll(function(){
$("div").css("margin-top", $(window).scrollTop())
});

Here is one way to solution.f https://jsfiddle.net/oravckzx/1/
$(window).scroll(function(){
$('.post-sidebar').each(function(){
var ScrollTopVar = $(window).scrollTop();
var OffsetTopVar = $(this).offset().top;
var OuterHeightVar = $(this).outerHeight();
var PositionVar = OffsetTopVar-(OuterHeightVar*1.1);
if (ScrollTopVar >= PositionVar) {
$('.hide') .css('background','green').css('font-size','12px')
$('.post-sidebar') .css('background','orange').css('font-size','12px')
$('.hide') .css('background','green').css('font-size','12px')
$('.post-sidebar') .css('background','gray').css('font-size','12px')
document.getElementById("demo3").innerHTML = ScrollTopVar;
document.getElementById("demo2").innerHTML = PositionVar;
}else {
$('.hide') .css('background','yellow').css('background','yellow')
$('.hide') .css('background','yellow')
$('.hide') .css('background','yellow')
document.getElementById("demo").innerHTML = ScrollTopVar;
document.getElementById("demo12").innerHTML = ScrollTopVar;
document.getElementById("demo13").innerHTML = ScrollTopVar;
document.getElementById("demo14").innerHTML = ScrollTopVar;
document.getElementById("demo2").innerHTML = PositionVar;
}
});
});
.red {height:100px;
background:red;margin-bottom:20px;}
.hide {height:50px;background:blue;margin-bottom:20px;}
.post-sidebar {
height:50px;
background:yellow;
margin-bottom:20px;
box-sizing: border-box;
display: block;
font: normal 700 34px Lato, sans-serif;
padding-right: 20px;
width: 452px;
}
.p {
font: normal 700 14px Lato, sans-serif;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="red"><p id="demo"></div>
<div class="red"></div><p id="demo12">ok
<div class="red"><p id="demo13">ok</div>
<div class="post-sidebar"><p id="demo2"></p><p>lizard</p></div>
<div class="hide"><p id="demo2">If reaches to chosen class in html, saves value of ScrollTopVar (as distance from top) to separate variable as eventually PositionVar (which is the distance from top to the chosen class in html, - and that specific distance depends of device which is doing it), and then does certain action if ScrollTopVar value matches or exceeds PositionVar value. Else sets it back if needed, if not including Else it remains as it once met the value. <p id="demo3"></div>
<div class="red"><p id="demo14">ok</div>
<div class="red"><p id="demo"></div>
<div class="red"><p id="demo"></div>

Related

Hide custom cursor on hover over selected div

I have a custom cursor with some hover effects on a page (wordpress). This custom cursor shall hide, when hovering a certain div, due to some text. Please take a look here: https://florianwmueller.com/work-test/
I tried a lot, including some javascript, but nothing works. Any idea?
I gave the pictures a special class: .no-cursor
Thankful for any help...
Add this script on your website.
<script>
var elements = document.getElementsByClassName("no-cursor");
var style;
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("mouseover", function() {
style = document.createElement("style");
style.innerHTML = "body.cursor-element-shape a { cursor: default !important; } .wpcc-active > .wpcc-cursor { display: none !important; }";
document.head.appendChild(style);
});
elements[i].addEventListener("mouseleave", function() {
document.head.removeChild(style);
});
}
</script>

True CSS Positioned Float

http://www.w3.org/TR/css3-exclusions/
Is what I need and IE supports it as can be demonstrated nicely at http://ie.microsoft.com/testdrive/html5/positionedfloats/default.html
however no other browsers seem to support the wrap-flow attribute.
How do I achieve a positioned float on any browser other than IE? (The "excluded block" can be at a fixed position, however everything surrounding it is dynamic in nature.)
Note I have tried the css "shape-outside" attribute: http://jsfiddle.net/ourk5mue/
-webkit-shape-outside: polygon(0px 280px, 100% 280px, 100% 500px, 0px 500px);
however the results are either unreliable (due to occationally disabling line breaking (as seen on chrome)), or not supported (on firefox).
It looks like I'll have to use javascript to manually relocate the DOM element in the document flow. Can this be done only with CSS?
Ended up doing something like this:
I execute a function that relocates the target block whenever the surrounding angular ng-repeat blocks change or if the window resizes.
app.directive('onFinishRender', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
scope.$emit('ngRepeatFinished');
});
}
}
};
});
var injectItem = function(ngRepeatFinishedEvent) {
if (typeof(ngRepeatFinishedEvent) !== "undefined") {
if (typeof(ngRepeatFinishedEvent.targetScope.item) === "undefined") {return;}
//other validation
}
var index = 2;
if (window.innerWidth < 1200) {index=1;}
if (window.innerWidth < 768) {index=0;}
var secondRow = jQuery('.items').eq(index);
var injectable = jQuery(".injectable").eq(0);
if (!secondRow.next().hasClass('injectable')) {
jQuery(".injectable:not(:first)").remove();
secondRow.after(injectable.clone().css('display', 'block'));
}
};
$scope.$on('ngRepeatFinished', injectItem);
window.addEventListener('resize', function() {
injectItem();
});
<span class="injectable" style="display: none;">sorta fixed position blah</span>
<span ng-repeat="item in items" on-finish-render class="item">
surrounding blah
</span>
this answer is based on Calling a function when ng-repeat has finished

Hide a whole div with CSS with part of it is empty

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.

Auto-resize an image in CSS without replacing HTML width / height attributes?

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

onfocus="this.blur();" problem

// I am trying to apply an "onfocus="this.blur();"" so as to remove the dotted border lines around pics that are being clicked-on
// the effect should be applied to all thumb-nail links/a-tags within a div..
// sudo code (where I am):
$(".box a").focus( // so as to effect only a tags within divs of class=box | mousedown vs. onfocus vs. *** ?? | javascript/jquery... ???
function ()
{
var num = $(this).attr('id').replace('link_no', '');
alert("Link no. " + num + " was clicked on, but I would like an onfocus=\"this.blur();\" effect to work here instead of the alert...");
// sudo bits of code that I'm after:
// $('#link_no' + num).blur();
// $(this).blur();
// $(this).onfocus = function () { this.blur(); };
}
);
// the below works for me in firefox and ie also, but I would like it to effect only a tags within my div with class="box"
function blurAnchors2()
{
if (document.getElementsByTagName) {
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].onfocus = function () { this.blur(); };
}
}
}
Thanks guys - I have gone for the css(a:focus):
img, a:focus{
outline: none;
}
It seems to be working right(tabbing is still working and the borders are gone when clicking) for me... in both ie and firefox. Will have to now retrofit some other links to use it...
Thanks again.
It's not recommended to blur. If all you're looking at doing is hiding the focus lines, use this instead:
a[i].onfocus = function () { this.hideFocus = true; };
This will work for all versions of IE. For other browsers (including IE8 in standards mode) you can set the outline CSS style to hide focus outlines:
a {
outline: none;
}
This would make your page much more keyboard friendly than blurring an element as it takes focus.
I would suggest using only CSS to remove the border.
img, a:active{
outline: none;
}
Or is there a specific reason why JS must be used?

Resources