Revert media query style in javascript - javascript

I'm trying to handle media queries in JavaScript. I'm using window.matchMedia query.matches to achieve this. The thing is, I'm using multiple media queries to adjust the CSS styling from the JavaScript, and it works when sizing the window down. However, when resizing the window up, I can't figure out how to revert the styles. Here's my code so far:
//attributes.size loops over two different sizes, e.g. 600 and 1000
let mQuery = window.matchMedia(`(max-width: ${attributes.size}px)`)
function resize() {
let change = {
//change.style is changing font size, not really important here
style: attributes.modifier.split("-")[0],
//change.by gets the multiple font sizes (e.g. 24 and 36) we are changing too when mQuery.matches
by: attributes.modifier.split("-")[1]
}
//This works
if (mQuery.matches) {
switch (change.style) {
case "f":
elms.style.fontSize = change.by + "px"
break
}
}
//Now how do I revert the style here? How can I "grab" the original font size? Usually it's just hard coded in, see this example: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_matchmedia
else {
switch (change.style) {
case "f":
elms.style.fontSize = ??
}
}
}
Therefore my question is: how can I revert the style back, after mQuery.matches (see the else statement). All the examples I've seen manually revert the style, which is hardcoded into the JS, like with this example: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_matchmedia. How can I avoid this and just revert the style back after the mQuery no longer matches?

Related

How to animate a button when it is visible for the user by scrolling

When you scroll on a page, the page shows an element, I want to be able to specify this element and
do code with it using JS, is this possible? I tried something but it had another problem..
What I tried was,
let section = document.getElementById('out');
window.onscroll = function() {
if (window.scrollY >= 678) {
document.getElementById('out').style.color = "red";
} else {
document.getElementById('out').style.color = "black"
}
}
I didn't use animate here, I just made sure it works, and it did, well almost, because if you zoom in/out it ruins it, I think that's because I got the 678 by going to the button and printing scrollY manually, is there anyway to make that automatic, so it works on any element I need?
I searched a lot and can't seem to find what I need, the solutions need jQuery, I need a solution only with html, css, and javascript.
In the future the solution will be css scroll timelines, but as that feature is at the time of writing experimental and is not supported by major browsers you can use intersection observers.
Quoted from MDN:
The Intersection Observer API lets code register a callback function that is executed whenever an element they wish to monitor enters or exits another element (or the viewport), or when the amount by which the two intersect changes by a requested amount.
To animate a component when it is in or out of view, you can give animated elements a .hidden class in your html markup and create an intersection observer which appends the .shown class to .hidden elements when they are in view.
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => entry.target.classList.toggle(“shown”, entry.isIntersecting))
})
const hiddenElements = document.querySelectorAll(“.hidden”)
hiddenElements.forEach((el) => observer.observe(el))
Then you can just apply transitions under a <selector>.shown css rule.

Javascript to use different value for mobile vs desktop in gallery

I have a slight problem with Javascript (or jQuery?).
Its part of a gallery from codrops.com. Its just a small part of it. Its the part Im having trouble with. (see it at kuglerdesign.com/gallery.html - still a site under construction)
// stops slideshow
_stopSlideshow = function( pause ) {
if( Gamma.isAnimating ) {
return false;
}
Gamma.isAnimating = true;
clearTimeout( Gamma.slideshowtimeout );
if( !pause ) {
Gamma.slideshow = false;
Gamma.svplay.removeClass( 'gamma-btn-sspause' );
Gamma.svMargins = Gamma.settings.svMarginsVH;
_toggleControl( Gamma.svclose, 'on' );
_toggleControl( Gamma.svnavprev, 'on', { left : 20 } );
_toggleControl( Gamma.svnavnext, 'on', { right : 60 } );
_svResizeImage( function() {
Gamma.isAnimating = false;
} );
However, there is a problem with the arrows that allow you to see previous and next picture. It happens when you tell the gallery to start the slideshow (which moves the arrows to -60px left and right (not seen here), respectively) and then stop the slideshow. The Javascript reads that it should move the arrows back into position to 60px right and 20px left.
But, on a mobile 60px is too much, and on a desktop 20px(for mobile) is too little (at start it uses CSS class, where it is different for mobile and desktop).
I was hoping to be able to write an if statement that would say if the screen is smaller than 760px, it would move by 20px, otherwise it would take 60px.
I wrote something like this:
_toggleControl( Gamma.svnavnext, 'on', { if (screen.width < 760) {right : 20;} else {right :60 } );
(rest of code still same)
Would that ever have a chancce of working without adding too much more code?
Short answer:
Put in a simple 'if' statement and abstract the values:
if (window.innerWidth < 760) {
var offsetLeft = 8;
var offsetRight = 20;
} else {
var offsetLeft = 20;
var offsetRight = 60;
}
_toggleControl( Gamma.svnavprev, 'on', { left : offsetLeft } );
_toggleControl( Gamma.svnavnext, 'on', { right : offsetRight } );
Long (more correct) answer:
Use a relative unit of space, like em or %. That way it'll always show correctly regardless of screen size. Using the fixed unit px, you'll have to hard-code different amounts for each possible screen size (which, nowadays, varies a lot what with tablets and phones in all shapes and sizes).
It looks like all of your CSS is currently using the px units and made responsive using JavaScript (I see some percentages but those look set using JavaScript as well). If you need an introduction to using scalable units of space (you should always use scalable units unless you have no other option, which rarely happens), I direct you to this excellent beginner's article by Kyle Schaeffer.
Alternatively, detect the device's screen dimensions and use a different CSS file based on that. This depends on your layout, of course, as it is increasingly more common to design in a mobile-first approach and simply scale up to desktop sizes using the aforementioned relative units. This design method removes the need of separate layouts for different devices.
Also, if all you're trying to do is hide the arrows temporarily, I suggest simply setting display: none in CSS using JavaScript to hide them and display: block to make them reappear. A simple way of doing this would be this:
var arrows = document.getElementByClassName('arrow'); // assuming the arrows have a common class name called 'arrow'
for (i=0; i<arrows.length; i++) {
arrows[i].style.display = 'none';
}
You can use this approach: Live Detect Browser Size - jQuery / JavaScript
Followed by updating the _toggleControl function within a check of the browser size.

Why is this while JavaScript loop infinite?

I hate doing this. This is THE small piece to end a large project and my mind is fried...
Here's the code. It checks to see if an element is overflowing and resizes the font. It is supposed to resize it until it doesn't overflow. The condition for the loop seems to be ignored and the browser freezes... I feel that I'm missing something crucial in how jQuery works here.
$.fn.fontBefitting = function() {
var _elm = $(this)[0];
var _hasScrollBar = false;
while ((_elm.clientHeight < _elm.scrollHeight) || (_elm.clientWidth < _elm.scrollWidth)) {
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
$(this).css('fontSize',fontSize+'px');
}
}
Thanks in advance.
Change:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))*0.95;
to:
fontSize = parseInt(fontSize.substring(0,fontSize.length-2))-1;
Here's a Working Demo. When the font size reached 10px, 10*.95 was 9.5 which the browser was rounding up to 10px. Thus infinite loop.
You need to step through your code in a debugger and actually check your condition values to make sure they are changing how you expect. My guess is _elm.clientHieght and _elm.clientWidth aren't actually changing.
var fontSize = $(this).css('fontSize');
fontSize = parseInt(fontSize, ...
The unit you get from font-size is not necessarily (a) pixels, nor (b) the same unit as you put in.
It's not specified what unit is used to return the length, but in many browsers it is currently points. Since points are smaller than pixels, the integer length will be longer, so you can quite easily keep on *0.95ing it forever.
Even if it were pixels, the browser could round the size up to the nearest pixel, making 95%-size the same size as 100% when you read it back. Or you could hit the minimum-font-size setting and you wouldn't be able to reduce it any more.
So instead of reading the current font size back on each step, keep the pixel size you want in a variable and reduce that variable each time. Then if you reach a predetermined lower bound for the value of that variable, give up.
You are probably running into an endless loop because the font size doesn't actually change. E.g. if the font size found is 10px you will update it to become 9.5px which is probably rounded back to 10px by the browser. In that case nothing changes and the function will keep running forever.
You've got an unrelated problem when you do
$('div').fontBefitting()
This will make the text in the first div fit it's box, then make the font size of all the other divs the same as the first. This does not sound like intended behaviour. You would hope that it would make each div resize its text and only its text to fit.
You need to change your code to this:
$.fn.fontBefitting = function() {
/* $.fn.* runs on a jQuery object. Make sure to return it for chaining */
return this.each(function() {
var fontSize = parseInt($(this).css('fontSize'));
while (this.clientHeight < this.scrollHeight ||
this.clientWidth < this.scrollWidth) {
fontSize--;
$(this).css('fontSize', fontSize + 'px');
}
});
}
You're checking to see if the clientHeight or clientWidth are LESS than the scrollHeight or scrollWidth, and if they are you are REDUCING the font size? It will never converge under those circumstances. You want to INCREASE the font size.

How to create a dynamic CSS-based interface using moving content tiles?

I'm trying to figure out what the best way would be to set up a website interface that has a large centre 'tile' (basically a div with rounded corners, a variable background image, and text on it) that acts as the hub of the interface, around which I have smaller tiles which are clickable as link, e.g. one tile will lead to a photo gallery etc... However I need these smaller tiles to be moveable i.e. I would like them to visibly whisk away off the screen (in a specific direction) before the next set of tiles enters the screen.
(Ideally they would be the same set of tiles, they would simply go off screen to 'change' as it were and come back as the new set of tiles - An ideal example would be of clicking on the photo gallery tile, all the main tiles whisk away off screen, to be replaced by more tiles representing individual photos in the gallery)
I have no issues with the CSS of round corners and positioning my tiles etc... but I'm currently trying to get the tiles to actually move using the code referenced here: Alter CSS class attributes with javascript?
I can't get it to work. I've set up one of my test tiles to make just one change to the width of another test tile using the above-referenced code when it detects a mouseover event on the div, but it appears not to work.
Here's my code, if you can spot any errors, but primarily I'd also like to hear if you have any better suggestions of reaching the design state I'm looking for:
var style;
function changeFoo() {
if(typeof style == 'undefined') {
var append = true;
style = document.createElement('style');
}
else {
while (style.hasChildNodes()) {
style.removeChild(style.firstChild);
}
}
var head = document.getElementsByTagName('head')[0];
var rules = document.createTextNode(
'.tiletest2 { border:4px solid #999; background-color:#999; width: 50px; border-radius:32px; }'
);
style.type = 'text/css';
if(style.styleSheet) {
style.styleSheet.cssText = rules.nodeValue;
} else {
style.appendChild(rules);
}
if(append === true) head.appendChild(style);
}
The onmouseover event looks like this:
<div class="tiletest1" onmouseover="changeFoo()">
<br/><br/>
SAMPLE left
<br/><br/>
Try using a JavaScript library like http://jquery.com/. You can also get plugins like http://jqueryui.com/ for the kinds of effects you're describing.
I agree with TimS to go with jquery, specifically you will want to use the .animate()function.
This will make it much easier on yourself since you can easily control the speed and time the animation plays and you may be able to easily remove div(s) with the .hide() function, which gives you many options of what kind of animation you could use to close it.

Opera: Altering img src attribute does not automatically update display?

When using javascript to swap images the HTML is updated fine but what Opera actually displays is not unless you scroll or resize the window. A picture of what happens when you scroll explains it best.
alt text http://img340.imageshack.us/img340/9455/87855188.png
Any ideas?
EDIT: The source of the problem seems to be that the image is inside a div that has float right.
EDIT2: This http://trac.dojotoolkit.org/ticket/3158 would suggest that it's a bug that was fixed and is back again.
Odd, I've never experienced problems like that before. I think that is a combination between browser and the graphics card / GUI, I've had exactly this behaviour before but in all sorts of applications (OpenOffice), not only the browser.
Ideas on how to maybe trick it into updating:
Set opacity to .99 and then back to 1
Change position by 1px (jerky though)
Set display to none and to block again (flickers, not nice, but to see whether it works)
Move it off the screen for a (milli)second and back again (probably flickers)
I have faced the same problem. This seems to be a bug related with Presto based Opera versions (< 12.5). The src attribute of the img elements seems to be updating correctly but the changes are not reflected to DOM. Triggering reflows are sadly not working. Only detaching and reattaching the node seems to fix the problem. I have tried following that led to no avail:
Change src to null, and then to new value,
Change src to null, change position (top/left etc), change width/height,
Trigger above with delay (i.e. 100ms delay between null and new value)
Performing various combination of above with any order.
The only way that correctly fixed the problem was detaching related node from DOM and reinserting. Here is the piece of code if anyone needs:
var isOperaPresto = this.navigator.userAgent.includes("Opera") && this.navigator.userAgent.includes("Presto");
if(isOperaPresto)
{
/* if browser is opera presto, updating image elements' sources will not upload the DOM visual.
So we need to do some hacking. Only thing that works is to remove and reAppend the relevant node... */
Object.defineProperty(HTMLImageElement.prototype, "src", {
enumerable: true,
configurable: true,
get: function() {
return this.getAttribute("src");
},
set: function(newSrc)
{
/*max-size confinement is required for presto if parent is display flex. Image will go out of its available size otherwise*/
this.style.maxHeight = this.style.height;
this.style.maxWidth = this.style.width;
this.setAttribute("src", newSrc);
/*we have to put this node back to exactly where we rip it from*/
var parent = this.parentNode;
if(this.nextElementSibling != null)
{
var reference = this.nextElementSibling;
parent.removeChild(this);
reference.insertAdjacentElement("beforebegin", this);
}
else
{
parent.removeChild(this);
parent.appendChild(this);
}
}
});
}

Categories

Resources