Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 12 of 12
  1. #1
    Administrator VIPStephan's Avatar
    Join Date
    Jan 2006
    Location
    Halle (Saale), Germany
    Posts
    11,382
    Thanks
    7
    Thanked 1,370 Times in 1,339 Posts

    Execute different stuff depending on function argument?

    I was under the impression that I could execute different functions/methods depending on the argument passed to a function but in the following simplified example this doesn’t seem to work:
    Code:
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <title></title>
            <meta charset="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <style type="text/css">
                html:before {content: 'large';}
                #container {padding: 70vh 0;}
                #container div {
                    height: 20vh;
                    border: 1px solid red;
                }
                @media screen and (max-width: 800px) {
                    html:before {content: 'small';}
                }
            </style>
        </head>
        <body>
            <div id="container">
                <div id="div1">div1</div>
                <div id="div2">div2</div>
                <div id="div3">div3</div>
            </div>
            <script type="text/javascript" src="js/jquery.min.js"></script>
            <script type="text/javascript" src="js/TweenMax.min.js"></script>
            <script type="text/javascript" src="js/ScrollMagic.js"></script>
            <script type="text/javascript" src="js/debug.addIndicators.js"></script>
            <script type="text/javascript" src="js/animation.gsap.min.js"></script>
            <script type="text/javascript">
                var calculateBreakpoint = function() {
                    return getComputedStyle(document.documentElement,':before').content.replace(/\"|\'/g, '');
                }
                var win = $(window);
                var breakpointCheck, breakpoint = calculateBreakpoint();
                var scrollFn = function(status) {
                    var controller;
                    if(status === 'destroy') {
                        if(controller) {
                            controller.destroy(true);
                            controller = null;
                        }
                    }
                    else {
                            controller = new ScrollMagic.Controller({
                                loglevel: 3
                            });
                            new ScrollMagic.Scene({
                                duration: 300,
                                triggerElement: '#header'
                            })
                                .addIndicators()
                                .setPin('#header')
                                .addTo(controller);
                    }
                };
                win.resize(function() {
                    breakpoint = calculateBreakpoint();
                    if(breakpoint == 'large' && breakpointCheck !== 'large') {
                        breakpointCheck = 'large';
                        console.log('large');
                        scrollFn();
                    }
                    else if(breakpoint == 'small' && breakpointCheck !== 'small') {
                        breakpointCheck = 'small';
                        console.log('small');
                        scrollFn('destroy');
                    }
                }).trigger('resize');
            </script>
        </body>
    </html>
    I’m working with the ScrollMagic library here (as part of a larger project which is why jQuery is also used) and am trying to invoke the functionality only for large viewports, and destroy any existing functionality if the viewport width is being decreased below a certain breakpoint. The destroying part doesn’t work and I’d like to understand why and learn a better way of doing this.

    I’ve got it working if I put the destroy method right into the “small breakpoint” part inside the resize function but that disconnects it from the creation and makes the code messy in my opinion.

    Any thoughts?
    Last edited by VIPStephan; Sep 8th, 2019 at 09:49 PM.

  2. #2
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,683
    Thanks
    5
    Thanked 529 Times in 515 Posts
    I don't see breakpointcheck actually being set.

    Though I'm wondering what you're doing that would warrant looking at the style from the scripting. That generally shouldn't be something needed. Never heard of "scrollmagic" but it looks like the type of crap I'd EXPLICITLY tell clients not to use.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  3. #3
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    4,002
    Thanks
    58
    Thanked 712 Times in 707 Posts
    PHP Code:
                var scrollFn = function(status) {
                    var 
    controller;
                    if(
    status === 'destroy') {
                        if(
    controller) {
                            
    controller.destroy(true);
                            
    controller null;
                        }
                    } 
    from what I can tell controller will always be undefined here so that if block will never be reached. I guess you could move controller into a higher scope which might be undesirable but you seem to be doing it with breakpoint and breakpointcheck anyway
    Last edited by xelawho; Sep 9th, 2019 at 05:18 AM.

  4. #4
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,683
    Thanks
    5
    Thanked 529 Times in 515 Posts
    Question: Why are you tracking the breakpointCheck multiple times? As in having both "breakpoint" and "breakpointCheck" separate variables when they are always (or at least should always be) holding the same values? Oh, I see, you're tracking the last value. Bad/confusing naming convention there.

    I know you might not want to hear this, but ditch the jQ -- even just as a test -- to see if the mental huffing midgetry is what's screwing you over.

    Instead of this derpitude:

    Code:
    var win = $(window);
    win.resize(function() {
    	// whatever
    }).trigger('resize');
    Do it the vanilla way:

    Code:
    window.addEventListener('resize', function() {
    	// whatever
    }, false);
    Garbage like jQ's idiotic "daisy chain" of functions return just overcomplicate the simplest of things. How the blue blazes anyone can call that BS "easier" or "simpler" or "better" is utterly beyond me.

    I simplified it down to this, and it works just fine:

    Code:
    <!DOCTYPE html><html lang="en"><head><meta charset="utf-8">
    <meta
    	name="viewport"
    	content="width=device-width,height=device-height,initial-scale=1"
    >
    <title>Resize demo</title>
            
    <style>
    	html:before { content: 'large'; }
    	#container { padding: 70vh 0; }
    	#container div {
    		height: 20vh;
    		border: 1px solid red;
    	}
    	@media screen and (max-width: 800px) {
    		html:before {content: 'small';}
    	}
    </style>
    
    </head><body>
    
    <div id="container">
    	<div id="div1">div1</div>
    	<div id="div2">div2</div>
    	<div id="div3">div3</div>
    </div>
    
    <script>
    
    	// dummy version just to check without the rest of the "junk"
    
    	function scrollFn(status) {
    		if (status == 'destroy') {
    			console.log('destroy');
    		} else {
    			console.log('no destroy');
    		}
    	}
    	
    	function calculateBreakpoint() {
    		return getComputedStyle(document.documentElement,':before').content.replace(/\"|\'/g, '');
    	}
    	
    	var breakpoint = calculateBreakpoint();
    	
    	window.addEventListener('resize', function() {
    		var newBreakpoint = calculateBreakpoint();
    		if (newBreakpoint !== breakpoint) {
    			breakpoint = newBreakpoint;
    			switch (breakpoint) {
    				case 'large':
    					scrollFn();
    					break;
    				case 'small':
    					scrollFn('destroy');
    					break;
    			}
    			console.log(breakpoint);
    		}
    	}, false);
    	
    </script>
    
    </body></html>
    If that doesn't work with your proper "scrollFn" then I'd assume the problem is in the other stuff you have there, and not the event handling or passage of parameters.

    Also it's 2019 not 2009, you can stop saying type="text/css" and type="text/JavaScript". I'd also suggest ditching the XML closures, and just declaring the calculateBreakpoint function normally if you're not going to override it... and don't forget the charset meta should go BEFORE any text-bearing elements, that includes both other META and the TITLE tag. I know it's a sample code, but if you can't form the HTML properly, don't even bother.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  5. #5
    Administrator VIPStephan's Avatar
    Join Date
    Jan 2006
    Location
    Halle (Saale), Germany
    Posts
    11,382
    Thanks
    7
    Thanked 1,370 Times in 1,339 Posts
    OK, I know your stance on jQuery and the like, deathshadow, but let’s disregard this for now because it’s definitely not the problem. Apparently I was misconceiving the scope of the controller variable? I thought the variable would be defined as soon as the “else” part of the condition was met, and therefore accessible to the first part of the condition, too?

    Ah, no, I see where I was wrong now. Since the function gets fired again at the small viewport breakpoint with the argument set, it will indeed never define the controller in the first place. Hm, need to think of a different approach then.

  6. #6
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,683
    Thanks
    5
    Thanked 529 Times in 515 Posts
    Quote Originally Posted by VIPStephan View Post
    I thought the variable would be defined as soon as the “else” part of the condition was met, and therefore accessible to the first part of the condition, too?
    Oh, I see what you meant to do. No, every execution of a function has it's own stack with locals on the stack. As such the variable the first time your scrollfn() runs won't be the same as the second, or the third. Every time you run the function, you get a new one. That's what "var" does. As does "let" and "const". (again why I think people need to learn at least a LITTLE ASM before being allowed near HLL's)

    The solution here would be to treat your function as an OBJECT, and then use "this" to store it on the object... and again if you're not actually going to redefine the function, declare it as a proper function not a variable.

    Code:
    function scrollFn(status) {
    	if (status === 'destroy') {
    		if (this.controller) {
    			this.controller.destroy(true);
    			this.controller = null;
    		}
    	} else {
    		this.controller = new ScrollMagic.Controller({
    			loglevel: 3
    		});
    		new ScrollMagic.Scene({
    			duration: 300,
    			triggerElement: '#header'
    		})
    		.addIndicators()
    		.setPin('#header')
    		.addTo(controller);
    	}
    }
    Though an even better approach might be to just make it a proper object.

    Code:
    var scrollTracker = {
    	controller : false,
    	destroy : function() {
    		if (this.controller) {
    			this.controller.destroy(true);
    			this.controller = false;
    		}
    	},
    	make : function() {
    		this.controller = new ScrollMagic.Controller({
    			loglevel: 3
    		});
    		new ScrollMagic.Scene({
    			duration: 300,
    			triggerElement: '#header'
    		})
    		.addIndicators()
    		.setPin('#header')
    		.addTo(controller);
    	}
    }
    Instead of passing the parameter, do scrollTracker.destroy() and scrollTraker.make(). This removes the unnecessary overhead of the "if" statement entirely.

    As a rule of thumb, TRY not to make function calls that do tons of processing when/if you can instead just call the correct version directly. You know what one you want going in, so separate them as object properties.
    Last edited by deathshadow; Sep 10th, 2019 at 12:33 AM.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  7. #7
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    4,002
    Thanks
    58
    Thanked 712 Times in 707 Posts
    One problem that I don't see getting addressed here is that you are treating the breakpoint like it is a binary state, but it's obviously not: the window could be bigger than the breakpoint (but not at full size) then get maximised which would cause another controller to get put on top of the one that already exists, and who knows what that would do.

    It reminds me of all that UA sniffing silliness we used to do before realising that feature detection was the way to go - if you want to test for something, test for that thing - not for some other thing that you think will tell you if the first thing exists.

    One (unsatisfying) solution that springs to mind would be to destroy the controller (if it exits) every time the window resizes and rebuild it only if your breakpoint condition is met. Sure there's some overhead there, but how often does a window get resized?

    This is why media queries are so handy when compared to JS create/destroy routines - you get zero side effects from showing a shown element or hiding a hidden element.
    Last edited by xelawho; Sep 10th, 2019 at 08:10 AM.

  8. #8
    Administrator VIPStephan's Avatar
    Join Date
    Jan 2006
    Location
    Halle (Saale), Germany
    Posts
    11,382
    Thanks
    7
    Thanked 1,370 Times in 1,339 Posts
    Thanks, deathshadow; I went with the object approach, and it works nicely.

    xelawho, this problem is being addressed in my initial example by checking the previous viewport width and only executing the function if the specified breakpoint is crossed. So, the controller will only be created once if the viewport is larger than the specified size (which, by the way, is created with a CSS pseudo element via media queries and then read by JavaScript). If you continue to enlarge the viewport it will not be executed again.

  9. #9
    Senior Coder xelawho's Avatar
    Join Date
    Nov 2010
    Location
    Here
    Posts
    4,002
    Thanks
    58
    Thanked 712 Times in 707 Posts
    oh, yes I see it now - sorry to muddy the waters

  10. #10
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,683
    Thanks
    5
    Thanked 529 Times in 515 Posts
    Q: Are you inserting the text that way in the invalid spot in the final, or is that a placeholder? Even if you hide it via CSS, there's the chance that if you actually use text there Google and Yandex might try to smack you for a content penalty. But working internationally I'm one of the few nuts to even consider Baidu and Yandex...

    I've never seen that done with text before. Usually my approach is:

    html:before { content:""; position:absolute; right:100%; width:35em; }

    So I can pull getComputedStyle(document.body,':before').style.width, where the width is declared as my media query breakpoint so it's easier to track which witch is which.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com

  11. #11
    Administrator VIPStephan's Avatar
    Join Date
    Jan 2006
    Location
    Halle (Saale), Germany
    Posts
    11,382
    Thanks
    7
    Thanked 1,370 Times in 1,339 Posts
    Quote Originally Posted by deathshadow View Post
    Q: Are you inserting the text that way in the invalid spot in the final, or is that a placeholder? Even if you hide it via CSS, there's the chance that if you actually use text there Google and Yandex might try to smack you for a content penalty. But working internationally I'm one of the few nuts to even consider Baidu and Yandex...

    I've never seen that done with text before. Usually my approach is:

    html:before { content:""; position:absolute; right:100%; width:35em; }

    So I can pull getComputedStyle(document.body,':before').style.width, where the width is declared as my media query breakpoint so it's easier to track which witch is which.
    Well yes, I’m adding the text “large” and “small”, respectively, to the html element via :before. I don’t see, however, how Google would care about a tiny word. But I take your approach as inspiration for the future.

  12. #12
    Senior Coder deathshadow's Avatar
    Join Date
    Feb 2016
    Location
    Keene, NH
    Posts
    3,683
    Thanks
    5
    Thanked 529 Times in 515 Posts
    Quote Originally Posted by VIPStephan View Post
    Well yes, I’m adding the text “large” and “small”, respectively, to the html element via :before. I don’t see, however, how Google would care about a tiny word. But I take your approach as inspiration for the future.
    Generated content outside of the BODY has been used in the past for content cloaking. I'm not sure if they still check for it, but they did for some time. Some screen readers may also read it and then stop cold in their tracks as "render content" in the head is assumed to be the first <body>, so your second <body> (the one in the actual markup) gets ignored. I think legacy IE also treated it as two instances of <body> which was a pain in the *** as document.body no longer pointed at your markup <body> but the CSS "assumed" one. Don't quote me on that though, it's been a while since I dealt with that.

    It's why other than as a parent of <body> for setting height/width 100% and overflow, one shouldn't try to style <html> even if HTML 5 made it "ok". It's a bit like wrapping anchors around "damned near anything" instead of just inline-level content. Just because 5 says it's ok, doesn't mean it's RELIABLE or even predictable cross-UA.

    ... and I do mean cross UA. A browser is a UA, but a UA isn't always a browser.
    “There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies and the other way is to make it so complicated that there are no obvious deficiencies.” – C.A.R. Hoare, The 1980 ACM Turing Award Lecture
    http://www.cutcodedown.com


 

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •