19
submitted 2 months ago* (last edited 2 months ago) by andioop@programming.dev to c/learn_programming@programming.dev

I just spent an hour searching for how I could have gotten an

Uncaught TypeError: Cannot set properties of null

javascript. I checked the spelling of the element whose property I was trying to set and knew that element wasn't null because the spelling was the same in the code as in the HTML. I also knew my element was loading, so it wasn't that either.

Turns out no, the element was null. I was trying to set " NameHere" when the element's actual name was "NameHere".

Off by a single space. No wonder I thought the spelling was the same—because all the non-whitespace was identical. (No, the quotation marks slanting in the second NameHere and being totally vertical in the first NameHere wasn't a part of the error, I am typing them all vertical and either Lemmy or my instance is "correcting" them to slanted for the second NameHere. But that is also another tricky-to-spot text difference to watch out for!)

And what did not help is that everywhere I specifically typed things out, I had it correct with no extra spaces. Trying to set " NameHere" was the result of modifying a bunch of correct strings, remembering to account for a comma I put between them, but not remembering to account for the space I added after the comma. In short, I only ever got to see " NameHere" written out in the debugger (which is how I caught it after like 30 repeats of running with the debugger), because everywhere I had any strings written out in the code or the HTML it was always written "NameHere".

I figured I'd post about it here in case I can help anyone else going crazy over an error they did not expect and cannot figure out. Next time I get a similar error I will not just check spelling, I'll check everything in the name carefully, especially whitespace at the beginning and end, or things one space apart being written with two spaces instead. Anyone else have a similar story to save the rest of us some time?

you are viewing a single comment's thread
view the rest of the comments
[-] andioop@programming.dev 5 points 2 months ago* (last edited 2 months ago)

Not sure if this question is too noobish for !programming@programming.dev so I posted it here just in case.

[-] Ephera@lemmy.ml 8 points 2 months ago

I mean, that's kind of the first big lesson you learn from experience, to put as little trust in humans as possible. My immediate thought was whether one could've put that name into a constant somewhere, so you only had to spell it correctly once or twice.

Frankly, that also kind of makes it difficult for me to answer your question, because when I fuck up like that, I don't remember it as a dumb mistake that I shouldn't do next time, but rather I see it as an indicator that my precautions against human errors aren't good enough. They shouldn't be easy-to-solve, they should be medium-difficult-to-never-see-them-again.

[-] andioop@programming.dev 2 points 2 months ago* (last edited 2 months ago)

I actually wrote it just once. It acquired the space like this:

I concatenate a bunch of strings together, and add a comma and space between them so I could get stringOne, stringTwo, stringThree etc. I later need to decompose that. I remembered I separated stuff with a comma, but forgot about the space following the comma and that is how I ended up having to deal with " NameHere" vs "NameHere" without having actually written NameHere several times in my code. Is there a better way to go about this?

I have also just read my post again and it explicitly contradicts "I actually wrote it just once". Not sure if I did write it multiple times and merely forgot as I typed this comment and claimed to write it just once, or if I just pretended I wrote it multiple times when it was only once so I could simplify explaining my problem. For the purpose of my question though, let us pretend I did write it once. I promise I am aware that strings that are frequently used should be made constant, although I could use more specifics on what "frequently used" is (more than once?) and I'm wondering if you actually should not really use strings at all and always go for constants.

[-] Ephera@lemmy.ml 2 points 2 months ago

I mean, it's hard to tell why you're doing what you're doing there. Sometimes with JavaScript and frontend code, certain crimes are just a necessary evil.

But yes, I am calling it a crime to concatenate strings and decompose them afterwards.

For one, human errors like what you did are possible.

You could mildly improve on that by pulling out two functions:

  • encode_blah_list_to_string()
  • decode_blah_list_from_string()

You could then at least define those directly below each other in the code, making it easy to define them, so they work together.
If you've already looked into unit tests, this would also be an incredibly easy and useful unit test to write. In your test code, you would define a list, then call the encode-function, take the result of that and call the decode-function, and then the result of that should be identical to the original list.

Having said that, encoding things as a string with custom logic like that, has another big problem:
It could happen that e.g. stringTwo contains "Hello, World". Suddenly, you'd be splitting in the middle of stringTwo, when you're decoding.

So, ideally you can avoid it completely and pass around the array directly.

If you can't, the next best thing would be to encode it in JSON. It will handle all these edge cases, and it's a clearly defined way of encoding and decoding, so you wouldn't even need to pull it out into functions.

If it's some custom format that you display to the user (and ideally the user is aware that commas are bad), then yeah, you would have to do the custom encoding and decoding implementation, like described above.

I am aware that strings that are frequently used should be made constant, although I could use more specifics on what "frequently used" is (more than once?) and I'm wondering if you actually should not really use strings at all and always go for constants.

Alright, so if it's a string where it matters that it's spelled exactly correctly and it will be used in more than one place in your codebase, then I would always pull it out into a constant.

If it's an unchanging value that you just pass to a library in one place in your code, like a timezone name, then I wouldn't usually pull it out into a constant, unless I feel like it helps to make this constant clearly visible at the top of the file.
For example, it rarely happens that a constant is unquestionably correct and unchanging, so making your choice prominently visible can be useful. Of course, if you're not running in a browser, then these kind of arbitrarily chosen 'constants' should probably be configurable in a configuration file.

If it's a UI description text, then it might make sense to pull it out into a constant, just to unclutter your code.
Or if you're working with a localization framework, then you'd have pseudo-constants for that anyways, which would get replaced with the respective language text at runtime.

But yeah, I definitely wouldn't say you should always go for constants. They add a layer of indirection, meaning someone reading your code won't know what the actual string is without checking the constant.
But if it needs to be the same value in two places, then this layer of indirection is definitely worth it and makes it easier to reason about your code.

I will also say that if we weren't talking about JavaScript, I would be much more contrarian to strings. Partially because JS is used in frontend a lot, where you do need to deal with strings anyways. And partially, because JavaScript is dynamically typed, so it just doesn't have as many tools to deal with strings otherwise.

In our backend Rust codebase at work, I do tell our juniors, if you're typing a quotation mark and you're not doing it for logging or error messages, then do consider, if that's really what you want. Usually, they want an enum instead, or if they really do need to pass around a string, then they should use a newtype, which is basically a typed wrapper, so you can't accidentally pass the wrong kind of string into a function.

[-] andioop@programming.dev 2 points 2 months ago* (last edited 2 months ago)

It is JavaScript!

I wanted to say just thank you so much for your feedback and help, I really appreciate it. I'll probably try to handle it as an array until final display to the user (tbh probably just me), where the commas and spaces will be used because that's how English works and seeing an output of stringOne,stringTwo,stringThree without the space would just irritate me a lot.

I am aware of unit testing and know I should use it. I also didn't use any frameworks and I'm not sure what I should use to test it when I didn't use Node.js or React or anything like that (not sure if "framework" is the right word). My only knowledge of them is that sometimes when I fork other peoples' projects I have to do stuff like npm install and npm run build and I have a vague overview idea of what those commands do (install dependencies, compile the stuff and run it). What I actually did to test things was just using the website. I let it go because it is small, under 200 lines of code, and probably will not expand very much.

Specifically the actual JS never has the string written down! I grab it from the HTML. Where it does show up several times: the element's name, ID (wonder if I can just wipe it out of the name), often its value, and in the element's label in the for attribute. Not sure what best practice is here.

[-] Ephera@lemmy.ml 2 points 2 months ago* (last edited 2 months ago)

I'll probably try to handle it as an array until final display to the user (tbh probably just me), where the commas and spaces will be used because that's how English works and seeing an output of stringOne,stringTwo,stringThree without the space would just irritate me a lot.

Yeah, sounds good. If you can keep it as an array and only have to encode it for displaying, that's great. It's only when you start decoding again, when the format starts to matter a lot.

not sure if "framework" is the right word

I would say it is. React is definitely a framework. Node.js is almost even a platform, because it also includes a runtime, but it also includes a framework, which is the relevant part that you're talking about.

There's no real good definition of "framework". Sometimes what people mean by that, is just a very big library, which has so many tools that your code is entirely reliant on it.
A definition, which I find is also often fitting, is that a framework takes over the control flow. Unlike a library, where you're the one calling library functions, a framework calls your code.

What I actually did to test things was just using the website. I let it go because it is small, under 200 lines of code, and probably will not expand very much.

I mean, that is probably fine.
Someone else (including future you) coming across the code would probably curse you for not documenting any of the intended behavior, but at 200 lines, you might have relatively little complex code.
But yeah, that's how you should think of unit testing, it's a specification documenting intended behavior. With the added bonus that it can automatically verify that your code meets this specification.

Having said that, I would absolutely recommend not letting it go. A small project like that is fantastic to learn how to do the whole project setup.
If you find out how to integrate a package manager / build tool like npm with this project, then you'll be able to tackle a bigger project next time around, because you can easily use JS libraries.
And if you then also find out how to use a unit testing library, like jest, your projects can become even bigger, because there's less code you have to manually test (nor manually specify intended behavior).

For npm, you might want to look up a separate tutorial. Their Getting Started guide talks about creating a user account and whatnot, which you don't need, unless you want to publish libraries.
For jest, their Getting Started guide looks pretty decent: https://jestjs.io/docs/getting-started

I grab it from the HTML. Where it does show up several times: the element's name, ID (wonder if I can just wipe it out of the name), often its value, and in the element's label in the for attribute. Not sure what best practice is here.

HTML is definitely less crucial to deduplicate. It kind of is like a load of constants defined in one place. When writing HTML, you want to try to choose the IDs, so that they remain unique and meaningful without having to change them all the time. But yeah, that will come with experience.

If someone decides to change an ID in HTML, then they need to be aware that they need to change it everywhere else, too. If you've done a good job giving it a rather unique name, it will also be trivial to do a text search across the whole project for it, and have it replaced everywhere.

If you do ever truly need to deduplicate HTML, there's various templating libraries available, but that's definitely not worth the overhead for deduplicating IDs.

[-] andioop@programming.dev 2 points 2 months ago* (last edited 2 months ago)

Wait no, I never had to change IDs. They are all unique and have been since I first defined them. I meant that the ID attribute of my element is the same as its name attribute and also its value attribute. Does any of the advice change given this?

Thank you!

[-] andioop@programming.dev 1 points 2 months ago* (last edited 2 months ago)

I am curious if it is viable to do it just pure JS or if I really need to learn some popular frameworks/libraries. I have no idea where this resistance comes from, guessing it's perhaps because "oh wow a whole framework" seems more intimidating than "learn to fix your code in a language you already know a little bit", as someone unfamiliar with frameworks. I should probably learn them anyways.

I do have some of the code commented but I also recently found according to https://refactoring.guru that this is bad?

Final thing: most resources I am aware of are for cleaning up object-oriented stuff. Wondering about resources for cleaning up non-object-oriented stuff and when I should and should not be doing object-oriented stuff, seeing as I did not write this raw JS object-oriented. (Yes, I know you can still kind of imitate some of the design patterns anyways, just curious.)

Also need to find out if if's okay to have the same thing appear twice in the HTML and how to put that in a constant if not, or if it would be better to programmatically generate it because a lot of it is pretty repetitive and the same string everywhere (except for the name attribute sometimes).

Again, thank you so much for your advice! I'll definitely be checking these resources.

[-] Ephera@lemmy.ml 2 points 2 months ago

I am curious if it is viable to do it just pure JS or if I really need to learn some popular frameworks/libraries. I have no idea where this resistance comes from, guessing it’s perhaps because “oh wow a whole framework” seems more intimidating than “learn to fix your code in a language you already know a little bit”, as someone unfamiliar with frameworks. I should probably learn them anyways.

I mean, I am primarily a backend guy. You should probably ask some frontend person. In my experience, if you get a job in a company, you're pretty much always going to be using one of these frameworks. From my little experience with Angular and React, it felt like you could learn them quite late and your JS knowledge would still apply. But these frameworks do have big ecosystems, with many supporting libraries, and the more projects you do with them, the more of these libraries you're gonna know, which can be invaluable experience.

I do have some of the code commented but I also recently found according to https://refactoring.guru that this is bad?

There is a bit of a culture war going on at the moment.
What everyone agrees on is that commenting why you implemented something a certain way (when it's not obvious) is always a good idea.
But describing what you did is ideally superfluous, because your code is readable enough to tell the same.
If it's not, then you should work on code readability instead, or try to reduce code complexity. In particular, in my experience most of the time, what people want to describe in a comment, should be a function name (by pulling the code out into a function) or a variable name or even a log statement. Because if it's not obvious where you're declaring that code, it's not going to be obvious where you're using that function/variable (or in the console output) either.

Comments also particularly have these problems:

  • Comments can get outdated. You might explain in a comment a certain behavior, then someone changes that behavior and doesn't update the comment. Then the comment is actively misleading.
  • Comments explain complexity rather than eliminate it. If you feel the need to explain what the hell a piece of code does, you're admitting that it's too complex. Sometimes, this just cannot be fixed. But many times, a refactoring is more useful, because others reading your code don't anymore have to understand the complexity, if it's gone.
  • Comments stop being read when there's too many. Devs will gladly not read comments, when they all just say useless stuff like //add user to list. If you leave out those comments, and instead only use them where you really need to point something important out, devs will be much more likely to read that.

But yeah, I am more on the side of disliking comments. Others might potentially say that my code is difficult to read, because they're used to a clear-text explanation, although I don't know, I haven't yet actually gotten that feedback. I do invest a lot of time on code readability, because in my experience it pays off.

Final thing: most resources I am aware of are for cleaning up object-oriented stuff. Wondering about resources for cleaning up non-object-oriented stuff and when I should and should not be doing object-oriented stuff, seeing as I did not write this raw JS object-oriented. (Yes, I know you can still kind of imitate some of the design patterns anyways, just curious.)

Right, next culture war. I wouldn't say I'm anti-object-orientation, but people definitely like to overdo it. And when I say people, I do also mean myself, when I came from a JVM language to TypeScript a few years ago.
And just to clarify, I distinguish between merely using objects (i.e. logic associated with data types) and object-orientation, which has the whole range of inheritance, encapsulation and whatnot.
Using objects is undoubtedly really useful (even if in an untyped language like JS, it might be less so).

But the full-blown object-orientation principles are in my experience, and I believe in the experience of many, primarily useful for writing big monolithic applications like one did in the 90s and 2000s. Since then, there has been a general learning and technological advances (better build tools, containerization, IDEs etc.), which means people tend to go for smaller or more modular codebases, which make the object-orientation principles far less useful. And since these principles do add complexity, they're being seen much less favorably.

Which is a long-winded way to say, I do understand why you're finding more resources for dealing with object-orientation, because there's more of a need for it.
Non-object-oriented code often goes for relatively simple structures, which just don't need as much refactoring. You might move a function or a data type into a different file, but that's mostly it.

As for advice on that:

  • Move your code around, so it's grouped semantically in a way that a human might expect.
  • Try to keep your control flow and data type references tree-shaped when possible. Unless you need it, you want to avoid circular references.
  • In particular, you might also want to keep your code kind of in 'layers' of abstraction. At the top, you've got high-level logic, which mostly just lists individual function calls. And then those functions do the actual work and might live in a separate file, in e.g. a sub-folder in the file structure.
  • Generally, it's a good idea to try to extract input/output code from your core logic. So, if you've got a backend where you need to send requests to, then you probably want a separate file/folder with just code that does the detail work for calling the backend. Things like converting data types into the right format, error handling and such, you don't want that cluttering up your core logic.
    Similarily, code for displaying to the user often concerns itself with details, like how to format a string, which you want to isolate from your core logic ideally, too.
    In my experience, JS or UI code in general is tricky for less seasoned devs in that regard, because it's often so easy to just quickly send something to the backend or UI in your core logic. And in particular, you may also hardly have any core logic to begin with. But yeah, in the long run, it's still good to do, because it reduces the code complexity.
    And frankly, even just having a file where all the backend calls are collected, has proven useful many times, because you can easily make adjustments when the backend API changes or you want to introduce authentication or things like that.

Also need to find out if if’s okay to have the same thing appear twice in the HTML and how to put that in a constant if not, or if it would be better to programmatically generate it because a lot of it is pretty repetitive and the same string everywhere (except for the name attribute sometimes).

Yeah, I tried to sneak that in via edit into my previous comment, but I think, you already responded before I did. So yeah, check up there.

[-] andioop@programming.dev 2 points 2 months ago

Thank you again so much for your advice. I read it all :)

this post was submitted on 20 Aug 2024
19 points (95.2% liked)

Learn Programming

1616 readers
1 users here now

Posting Etiquette

  1. Ask the main part of your question in the title. This should be concise but informative.

  2. Provide everything up front. Don't make people fish for more details in the comments. Provide background information and examples.

  3. Be present for follow up questions. Don't ask for help and run away. Stick around to answer questions and provide more details.

  4. Ask about the problem you're trying to solve. Don't focus too much on debugging your exact solution, as you may be going down the wrong path. Include as much information as you can about what you ultimately are trying to achieve. See more on this here: https://xyproblem.info/

Icon base by Delapouite under CC BY 3.0 with modifications to add a gradient

founded 1 year ago
MODERATORS