Weather App help -- toggle Fahrenheit and Celcius

My code will switch from the default Fahreneit to Celcius, but not back from Celcius to Fahrenheit. Below is my code. I’m sure there are much cleaner ways to make this switch so any help is welcome. (’.temp’ is the class name of the div where I am inserting the HTML).

//variable for fahrenheit
var temperature_F = Math.round(json.main.temp) + ‘’ + ‘°F’ + ‘’;

//variable for celcius
var temperature_C = Math.round((json.main.temp - 32) / 1.8) + ‘<a=“unitSwitch”>’ + ‘°C’ + ‘’;

//‘click’ function checks the ‘text’ of items with the class ‘unitSwitch’, then changes the HTML accordingly

$(".unitSwitch").click(function(){

        if ( $(".unitSwitch").text() === '°F' ) {

          $('.temp').html(temperature_C);
        }

        else if ( $(".unitSwitch").text() === '°C' ) {
          $('.temp').html(temperature_F);

        }

   });

A CodePen link will be much more helpful in helping you with this issue.
P.S. inside the click call back for the $(".unitSwitch") element, you can just reference it by $(this), the code will look a bit nicer and run faster

@bartonyoung this isn’t necesarrily the only way, but I think you might find it easier, if, instead of setting the variables above which contain equations (though this could work), if you make the conversions actually happen within the function. This way, you might be able to do something like, setting a flag that changes every time you do a conversion (which might be stored in a separate variable or in an html attr),

let flag = "convert to F";
if (flag == "convert to C") {
    /* apply this conversion and set DOM  */
} else {
    /* apply this conversion and set DOM */
}  

EDIT: I guess this seems to be pretty much what you’re doing. Can you post a link to your pen as @vdineva mentioned?

I did it kind of similarly:

$("input:checkbox").change(function() {
                                var ischecked= $(this).is(':checked');
                                if(!ischecked)
                                  $.getJSON("https://ipinfo.io/json", function(a){
                $.getJSON("https://api.apixu.com/v1/current.json?key=4ee2f4142bc942d18a9140825160511&q=" + a.loc, function(a) {
              $("#weather-data").html(a.current.temp_c + "°");
            });
              });
               
               if(ischecked){
                 $.getJSON("https://ipinfo.io/json", function(a){
                $.getJSON("https://api.apixu.com/v1/current.json?key=4ee2f4142bc942d18a9140825160511&q=" + a.loc, function(a) {
              $("#weather-data").html(a.current.temp_f + "°");
            });
              });
               }
                            });

pretty much I used a boolean to see if it was checked or not.

Based on the js code you have provided, looks like you have 2 elements on the page with class “.unitSwitch” (one to switch to C and one to switch to F). If that is the case then “$(”.unitSwitch").text()" will return the text for both elements and will never be ‘°F’ or ‘°C’. Changing within the click callback function $(".unitSwitch") to $(this) will fix it.

Thanks for the response. I’ve been creating this app locally on Sublime, and when I tried to post it on CodePen, it wouldn’t load at all. I’ve seen the other threads regarding CodePen, google and GeoLocation, so I’m guessing it has something to do with that.

EDITED TO ADD : Here is the ‘working’ app on codePen. http://codepen.io/bartonyoung/pen/ZBBvyG

@bartonyoungOk, so I see you figured out the first issue of getting your app to load in codepen (adding the jquery dependency) - you can do that much easier here by the way:

Now, I really think your problem is what @vdineva pointed out in his/her (sorry not sure) last post - you have two elements that are essentially the same. Try just getting the temp from the API once, setting it to the dom, and then changing that element with your function. Does that make sense? Rather than replacing the element with itself every time you change the unit.

I’m going to hesitantly disagree with @no-stack-dub-sack here, though I can’t say conclusively. I spent way too long trying to figure out what the problem with this was, and while I don’t understand what the exact problem is, it does seem to be related to the $tempBox.append… line of code. But the issue is not with .text(). In fact, your function properly changes the code, but seems only able to fire once. After one click, the whole “$(”.unitSwitch").click function no longer functions. I have no idea why this is, but messed around a lot with console.log() and that’s what I found.

I coded in a static button to do the same thing and that worked just fine. I have a feeling that .append()ing all that html is somehow making it hard for the $() to find its target. My suggestion would be to just put that stuff in as HTML (and not generate it with javascript) and then change the text, rather than trying to make whole new elements. I’m almost positive that would fix the problem.

How to make your code work as is I don’t really know, and why it won’t work is completely beyond me. It really does seem like it should, if it’s any consolation that another newbie thought it made sense.

@gtrabbit, not a bad analysis, but @bartonyoung I have to stick to my guns on this one - and perhaps I wasn’t explaining myself properly and that could be part of the problem, but what I know for sure is that the issue def is not with the append() function:

Try just getting the temp from the API once, setting it to the dom, and then changing that element with your function.

What I meant was, instead of setting the temp in 2 different ways from the start, do something like this:

var temp = Math.round(json.main.temp);

then, you can set that to the DOM much like you were doing:

$tempBox.append(
    '<div class="temp">' + temp +'</div>', 
    '<a class="unitSwitch">' + '°F' + '</a>'
    );

Now, perform the conversion within the function based on the one temp that is already loaded up. This might be giving away a bit too much, but the code could look something like this:

$(".unitSwitch").click(function(){
    if ( $(this).text() === '°F' ) {
        $('.temp').html(/*CONVERT HERE*/);
        $('.unitSwitch').html(/*SWITCH UNIT HERE*/)
    } else  {
        $('.temp').html(/*GET TEMP FROM API AGAIN HERE*/);
        $('.unitSwitch').html(/*SWITCH UNIT HERE*/)
    }
 });

Using this approach, I came up with a working version in your pen that switches well back and forth. Keep in mind, when I did this project, I went about it in a totally different way - so there are many solutions to this. But this is one way to keep most of the code you have, and get the feature to work.

@bartonyoung thanks for posting a CodePen. The issue is that you’re appending dynamically the ‘.unitSwitch’ element and the $(document).click event will be added only to the available ‘.unitSwitch’ element when the page loads. To make your code work you just have to update the

$(".unitSwitch").click(function(){
  ...
})

to

$("body").on('click', '.unitSwitch', function(){
  ...
})

and this way the click event will get triggered to the dynamically added ‘.unitSwitch’ element.

However, your code is not very efficient and I’d suggest you use the recommendations from the other guys above.

1 Like

Thanks for the response. I’m still struggling with this one. I’m not grasping why I want to use ‘.html’ instead of .text() for the link if I’m simply altered the text inside of a link. Either way, I’m able to change the number value of the temperature but not the unit.

@bartonyoung Ok, I’m going to just give you the answer - but you have to promise you’ll analyze it to figure out why your code wasn’t working and why this is better. I’ll tell you what to remove, then give you working code to replace it with.

Take these lines and delete them:

    //variable for temp in Fahrenheit
    var temperature_F = Math.round(json.main.temp) + '<a class="unitSwitch">' + '°F' + '</a>';
    //variable for temp in Celcius
    var temperature_C = Math.round((json.main.temp - 32) / 1.8) + '<a class="unitSwitch">' + '°C' + '</a>'

Then delete these lines:

$tempBox.append('<div class="temp">' + temperature_F  +'</div>',
                 '<div class="clouds">' + clouds +'</div>',
                 '<img src='+icon+' alt="" class="img-thumbnail">'
                  );

Then delete these lines:

//ALLOWS USER TO TOGGLE BETWEEN FARENHEIT AND CELCIUS
       $(".unitSwitch").click(function(){

            if ( $(this).text() === '°F' ) {

              $('.temp').html(temperature_C);
            }

            else  {
              $('.temp').html(temperature_F);

            }

       });

Then, where the above lines that you just deleted were, add all of this:

var temp = Math.round(json.main.temp); // gets temp from API

$tempBox.append(
    '<div class="temp">' + temp +'</div>', // Displays temp in F on load
    '<a class="unitSwitch">' + '°F' + '</a>', // Defines the unit for the callback function below to grab on to
    '<div class="clouds">' + clouds +'</div>',
    '<img src='+icon+' alt="" class="img-thumbnail">'
    );

$(".unitSwitch").click(function(){
    if ( $(this).text() === '°F' ) { 
        $('.temp').html(Math.round((json.main.temp - 32) / 1.8)); // Replaces F with temp converted to C
        $('.unitSwitch').html('°C')
    } else  {
        $('.temp').html(Math.round(json.main.temp)); // Replaces C temp with original temp by calling the API again
        $('.unitSwitch').html('°F')
    }
 });

The code placement might not be the best, and you might have to work with the styling a bit to make everything look right, but if you deleted all the lines I pointed out, and replaced with the above, the code should work (works well when I do it in Chrome).

Just make sure you take a look at your previous code and see why it’s not working. Your code is a bit funky, but it looks like it should work as is, but I think the problem is having those 2 elements assigned to variables. This is essentially doing the same thing, but in a less roundabout way. Let me know if you have success with this and if you have any other questions, I will do my best to answer what I can.

This could also be done by calling the API only once when the page loads, rather than getting the temp every time you click, but it would require a slightly different approach. This should be a good solution for the way you currently have things set up.

Thanks for all your help. I thought that I had tried the exact code you posted above, but apparently I was doing something wrong. I know that a problem with one version of my code was that, in attempting to have the temperature value and the anchor containing the temp unit all within one div, it was causing the entire div to be rewritten completely when I clicked. Maybe hard to describe in words.

One question about calling the API every click. As I’m new to APIs and jQuery, I’m not grasping how I am making more than one call to the API on every click. I was under the assumption that one call is made on page load, then simply stored in the ‘json’ object within the function.

Dealing with this problem revealed to me a potential flaw in my approach to this and problems like it. Perhaps the flaw is that when, after banging my head against it for a while, I should step back and re-evaluate to see if there is a different, easier way to set it up entirely. I feel that simply creating button that would set the value of the div would have been much easier and more straightforward.

If there are any basic best-practices/ obvious ways to improve the code as a whole, that info would be greatly appreciated.

Thanks again.

This explains why I was finding what I did…

@bartonyoung, [quote=“bartonyoung, post:13, topic:56397”]
after banging my head against it for a while, I should step back and re-evaluate to see if there is a different, easier way to set it up entirely.
[/quote] I totally get you on that. I was more than half-way through the wikipedia viewer and felt it had become such a mess that I ended up deleting the whole thing and started from nothing. Turned out way better that way. I intend to go back and re-write all the projects from the ground up once I finish the advanced projects. Insofar as best practices, I’m certainly not the one to ask. My code usually ends up working, but looking like a total mess. I’m hoping that goes away with time…

@bartonyoung

Now that you say this, I see that you might be right, I’m certainly not an expert, so like @gtrabbit, I might not be the best person to ask. I’m not a professional dev, so my understanding is probably not much deeper than yours, and in fact, on that point, your understanding might be deeper.

So there might be no difference, as I guess you would need to reload the page, or at least call $.getJSON again for the temp to update. We’re all learning together which is the beauty of this community! I’m just trying my best not to steer people in the wrong direction by taking on answering questions like these.

More importantly, I agree with you that sometimes if something isn’t working, take a step back and look for a simpler solution. Part of my answer may have been wrong I suppose, but the key was the simpler approach.