Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Creating my first page using JavaScript and so doing this by reading things on W3Schools and whatever comes up on Google.

Two really stupid questions...

code:
  x.setAttribute("id", "mySelect");
1. Is it just standard to call all elements before we add them to the DOM x? I'm used to giving all objects a meaningful name, although I guess it's just create + add so it matters less?
2. Is this purely stylistic? The second one seems a lot faster to type. Are there certain attributes that can't be set using properties (which I think is what you'd call the second one..)
code:
  x.setAttribute("id", "mySelect");
code:
x.id = "mySelect";

Adbot
ADBOT LOVES YOU

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Thinking purely logically rather than JavaScript (I'm a newbie too), if you had a monospace font then you'd know how many characters fit on a line. You could then do option 2 and remove /append say 120 characters at a time taking into account where words end.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Someone in the make a small apps thread made a project for me. I'm now going to fiddle with it to learn JavaScript and tweak some things.

He made it with everything inside a script tag on the html page.
I decided to move it to a linked .js file rather than be in the script at the bottom of the page.

Everything works, other than these three lines. If I move them to the .js file then they don't seem to run?

code:
document.getElementById('next').addEventListener('click', next);

document.getElementById('swap').addEventListener('click', swap);

document.getElementById('restart').addEventListener('click', init);



When I do, the buttons (next/swap/restart) buttons no longer do anything.

My guess is that it runs the script linked to in the head before the buttons are made? Does that seem a reasonable choice? If so, any suggestions on how to make it happen after the buttons have been added so the eventListeners can be added?

If not, why else might it be?

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

teen phone cutie posted:

where in the html doc did you include your script? if it's at the top of the html, then yeah the elements haven't rendered yet

JS scripts typically are best placed under the <body /> tag

e: re-read the post. yeah just put it under the body

Ah thank you for that. I just thought that script links to JS like links to a CSS file go in the head. Is that just a misconception or is there some reason why you would put them in the head?

I moved it down to after the body and all is resolved.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
I decided that it was time to learn JavaScript. I grabbed WebStorm as an IDE (teacher so educational licence for free) and have bought a course on Udemy (https://www.udemy.com/course/the-complete-javascript-course/). I've hobby coded in Python for a while so just going through the fundamentals now and they're going to be rather similar I'm sure.

The video was just about template literals and they remind me of f-strings in Python which are so lovely. In the video they mention that some people just use `` for every string moving forward. A quick search seemed to find some arguing that it's a bad idea (performance makes it slower as a blank template string literal needs parsing whereas a string in quote marks doesn't?) but just curious for best practice? I might end up moving towards using JS for a job so would rather avoid bad practice.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

teen phone cutie posted:

typically any company would have a linter and formatter, so more often-than-not, you'll have your quotes converted to either single or double quotes when formatting.

i think eslint (popular linter) and prettier (popular formatter) comes out of the box with rules for double quotes, so i'd go with that unless you need the backticks to add variables

Thanks, got prettier & eslint installed and leaving with the default options. As you said, if I get a job then whoever I'm working for will explain their prettier settings to me - that makes sense.



Next JavaScript silly question... my previous JS experience was when I was writing a lot of Python in Selenium to scrape webpages. When doing this I got really tired of finding the elements so I wrote myself plenty of helper methods and frequently saved the element as a variable and referred to it that way. Is this seen as poor practice?

JavaScript code:
// that first part got really tedious when I keep manipulating the same element
document.querySelector('.score').textContent = '5';

// so something like this instead
const scoreEle = document.querySelector('.score');
scoreEle.textContent = '5';

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

AlphaKeny1 posted:

I'm also glad you used const instead of let to assign the DOM node to a variable

edit: correct me if I'm wrong but I think your first example is a holdover from jquery where you'd have to query the selector each and every time you want to do something? Don't use jquery.

Thanks for that feedback. Given it a bit of a read after your comment, seems to suggest using document.getElementByID() and siblings unless complex in which case maybe querySelector(). Seem reasonable?

edit -- question...

JavaScript code:
function doStuff({ team1 = "Man U", team2 = "Chelsea", score }) {
  console.log(`${team1} played ${team2}. The score was ${score}`);
}
My understanding of this is that an object is passed in to the doStuff function, and then it destructured so it explicitly has access to team1, team2 and score (assuming they were properties of that object and if not they'll exist as undefined). If I wanted to later access the object itself, what's the best way to do that? Playing around in Webstorm I can see it exists in arguments[0]. Is there anywhere else? Mainly out of curiosity than thinking it is good practice.

Sad Panda fucked around with this message at 19:21 on Feb 21, 2023

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
This intro to JS course I'm doing has just introduced the unary plus operator and suggested going through the code we've worked on to change it from Number() to the + to convert because 'it is cleaner'. That lead to a definite bit of denial from me and the Python mindset of 'Explicit is better than implicit.' Surely Number() is more readable and more explicit? What's the advantage of using what to me could easily be mistaken for a seemingly random plus sign that was meant to concatenate or perform addition?

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
When you're coding, how do you decide which version to aim for? This course I'm working through is often explaining the original way to do it, and then how it can now be done. It used to be done by ... but in ES6/ES2020/ES2022 this new functionality was added to make it easier to do. One example being how to get the last element of an array.

JavaScript code:
arr[arr.length-1]; // traditional
arr.at(-1); // added in ES2022

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

MrMoo posted:

In WebPack land it’s almost always been: develop for the latest and transpile to ES5, and possibly ES6 if generous.

I’ve been perusing the VSCode base, and there is a lot of ES5-only code due to Electron being clunky.

Interesting. So does this mean that the main advantage of new features is mainly that they provide a simpler way of accomplishing a similar task rather than adding actual new functionality? This makes it faster for developers but doesn't change things in the backend?



Other question...

The course I'm doing has some HTML like this...

HTML code:
      <nav class="nav">
        <img
          src="img/logo.png"
          class="nav__logo"
        />
        <ul class="nav__links">
          <li class="nav__item">
            <a class="nav__link" href="#section--1">Features</a>
          </li>
...
and uses this code to model the idea of event bubbling

JavaScript code:
const randomColor = () =>
  `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`;

document
  .querySelector(".nav__link")
  .addEventListener("click", function (e) {
    this.style.backgroundColor = randomColor();
    console.log(`link ${e.target}`);
  });
document
  .querySelector(".nav__links")
  .addEventListener("click", function (e) {
    this.style.backgroundColor = randomColor();
    console.log(`container ${e.target}`);
  });
document
.querySelector(".nav")
.addEventListener("click", function (e) {
  this.style.backgroundColor = randomColor();
  console.log(`nav ${e.target}`);
});
It says that when an event takes place on a node, the event comes down the DOM from root to the target. Then it bubbles up. On its way back up this leads to the listeners on the parent nodes being triggered and changing to a random colour.

But if I change the click event to a mouseenter event, that doesn't trigger the parent nodes to change colour.

To me, that makes it seem less like the event is bubbling up, and more like it triggered them all because in clicking on the eg the a tag, I also clicked on its parents too. Whereas mouseentering the a tag didn't see me mouseenter the parents.

Am I wrong? Do only certain events propogate/bubble?

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Possibly a really stupid question. I'm looking at React for the first time ever. There is a paginationControl component. When you press the component, it calls the left/right pagination function and adjusts the index by the size of the page.


JavaScript code:
const Paginator = (props) => {
  const pageSize = 10;
  const [leftIndex, setLeftIndex] = useState(0);
  const [rightIndex, setRightIndex] = useState(pageSize);

  const handlePaginateRight = () => {
    if (rightIndex > numberOfResults) return; // guard clause - although not necessary given button is disabled lower down

    console.log(`paginating right from:${leftIndex} ${rightIndex}`);
    setLeftIndex(leftIndex + pageSize);
    setRightIndex(rightIndex + pageSize);
    console.log(`paginating right to:${leftIndex} ${rightIndex}`);
    props.setPaginatedRecords(
      props.filteredRecords.slice(leftIndex + pageSize, rightIndex + pageSize) // This feels like a hack to me
    );
  };
As you can see, the left + right index are set, and then it should set the paginated records by slicing the filtered records at the appropriate point. However, it seems to use the original leftIndex/rightIndex which is why I've had to fix it with the slice leftIndex+pageSize, rightIndex + pageSize instead of what I assumed would worked originally (the state of leftIndex/rightIndex.

Both the console logs output the same thing, even though the second is after the states should have been updated. This is literally my first time looking at React (got to find myself a Udemy course soon) so I could missing something fundamental like 'the value from the state is static throughout the function call and would need to be done in a separate call' or 'it just hasn't updated fast enough' (tried to make it async and add awaits but that didn't seem to help).

Sad Panda fucked around with this message at 18:25 on Apr 3, 2023

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Thank you for all those really helpful comments. Going from synchronous imperative to asynchronous declarative programming is definitely going to be a fun jump. Got that bug fixed and a few more.

I've finished my JavaScript course on Udemy. It was useful to go over the fundaments of the language (and also how it works behind the scenes) but the projects I made were mainly 'here is the HTML, CSS and a flowchart for how the site should work now lets code it together'. I generally paused it to try to create it myself to avoid just watching someone type but I think my next step will be more something like theodinproject as its more 'here is a project go try it, you will definitely need to go read the docs'.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
I'm looking at JS for the first time in a while and usign Puppeteer to do some scraping. It looks at the three URLs it is given and finds more links on them and should add them to my array to be dealt with.

JavaScript code:
  const URLsOfInterest = [
    "https://www.blahblah.com", "https://www.blahblah2.com", "https://www.blahblah3.com"
  ];

  let URLsToScrape = [];
  // Iterate through URLs of interest finding new pages that need scraping
  for (const siteURL of URLsOfInterest) {
    await page.goto(siteURL, {
      waitUntil: "domcontentloaded",
    });
    const cardSelector = ".card.item";

    URLsToScrape.push(
      ...(await page.$$eval(cardSelector, (elements) => {
        return elements.map((ele) => ele.href);
      }))
    });
  }
Is there a neater way to write that push part at the bottom? Putting that spread operator at the start so I get the individual items back rather than the whole array looks a bit ugly in my mind.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
I'm using eslint-config-airbnb-base. In my code I have optional chaining to deal with a possible querySelector not existing.

JavaScript code:
        function getStatus(record) {
          return (
		return record.querySelector('.product-mark')?.innerText || null          );
        }
It works fine but eslint says 'Parsing error: Unexpected token .'


https://stackoverflow.com/questions/61628947/eslint-optional-chaining-error-with-vscode this mentions about using eslint of 7.5 or higher but my npm list is...

code:
@google-cloud/functions-framework@3.3.0
 crypto@1.0.1
 env-cmd@10.1.0
 eslint-config-airbnb-base@15.0.0
 eslint-plugin-import@2.29.0
 eslint@8.53.0
 express@4.18.2
 md5@2.3.0
 mongodb@5.9.1
 puppeteer@19.11.1
If there's a better way to write that code, I'll also take that advice! I'm rather new to this. It seemed like an 'elegant' way to do it in an easy to understand line instead of having to do eg...

JavaScript code:
function getStatus(record) {
   const status = record.querySelector('.product-mark');
   return status ? status.innerText : null;
}

Sad Panda fucked around with this message at 16:53 on Nov 4, 2023

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

Doom Mathematic posted:

You probably don't have ESLint configured to understand the optional chaining operator, ?.. Take a look at your ESLint configuration, and make sure you have the parserOptions.ecmaVersion option configured to a version of ECMAScript which actually had that operator. The default value is ES5, which did not. In fact, it's probably best to just put 'latest' here.

Note that if ESLint doesn't recognise ?. it's not going to recognise ?? either. Also, you need to work out what the correct behavior is if innerText is an empty string. Should your code return the empty string? Then you can use ??. Should it return null instead? Then you should use ||.

Thanks - that was it. ESlint, prettier & airbnb config were all arguing with each other. Now got that setup - thank you.

Trying to make that happy is another fun thing. For example, ESLint says no for of loops & no await in loops. This breaks both of them.

JavaScript code:
  for (const [index, URLToScrape] of URLsToScrape.entries()) {
    console.log(`Scraping URL ${index}/${URLsToScrape.length}: ${URLToScrape}`);
    await page.goto(URLToScrape, {
      waitUntil: 'domcontentloaded',
    });
    more logic
}
For me that's just fine. I use for of because I want to iterate through a list of 3000 URLs that I've scraped and want them to do it one after another. It uses one page object (a puppeteer.launch.newPage()) and makes its way through that list.

Am I being wrong in ignoring ESLint for that? https://eslint.org/docs/latest/rules/no-await-in-loop the 'when not to use it' part at the bottom of this seems to suggest my case makes sense.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Following a beginner React course. Created this rather ugly to-do app. https://codesandbox.io/p/sandbox/todo-almost-forked-znfh4d?file=%2Fsrc%2FApp.js%3A187%2C1

Adding a person works.
Removing a person works.
Rendering the to-dos works.
Ticking off to-dos... is a work in progress and I'm not sure why.

When I tick a task in the ShowTodosForm, the count goes down in the sidebar. And if I click to a different person and back to that person, their todos are fine. However, if not then the item doesn't go away - and if I click to the other person the first item is ticked for them too.

I'm not really sure how to diagnose this so would love a pointer. Thanks.

Sad Panda
Sep 22, 2004

I'm a Sad Panda.
Thanks for that. I've changed the key, and that does indeed stop the same ID checkbox being ticked. It still doesn't immediately pop that item off my to-do list - I've coded it so when I check it, it pops it from the list.

JavaScript code:
  function handleTaskChecked(taskID) {
    const updatedPeopleData = peopleData.map((person) =>
      person === selectedPerson
        ? { ...person, todos: person.todos.filter((todo, i) => i !== taskID) }
        : person
    );
    setPeopleData(updatedPeopleData);
  }
It correctly sets the array to be a new version with the todo item removed, but doesn't re-render that version of the todo list. What's causing that? This also means that if I try to check multiple items off, it won't work. I assume that's because the rendered state in my todo list form is stale?

smackfu posted:

Related to that, you are using an uncontrolled input for the checkbox, so if it is reused, it will stay checked.

So passing in a value for the checked attribute might fix it.

Thanks - I was wondering about that. To me I wasn't sure what value to assign to it. If I had a boolean of completed, then that'd make sense to toggle it. As is I just want to pop it so unsure what value would work for it.

Sad Panda fucked around with this message at 21:44 on Feb 12, 2024

Adbot
ADBOT LOVES YOU

Sad Panda
Sep 22, 2004

I'm a Sad Panda.

Ima Computer posted:

Even though you're updating the state in peopleData, you're not updating the state in selectedPerson, which has a copy of the old person object.

Instead of storing a copy of the entire person object in state, try storing a selectedPersonId, and then use a memo to derive the selected person from selectedPersonId and peopleData whenever either of them changes:
JavaScript code:
const [peopleData, setPeopleData] = useState(initialData);
const [selectedPersonId, setSelectedPersonId] = useState(undefined);
const selectedPerson = useMemo(() => {
  if (selectedPersonId === undefined) return undefined;
  return peopleData.find(({ id }) => id === selectedPersonId)
}, [peopleData, selectedPersonId]);

Thanks for the push. I'd not learnt about useMemo yet so I fixed this instead by...

Storing the person's ID not the person. This meant in handleTaskChecked I could do it like this.
JavaScript code:
  function handleTaskChecked(taskID) {
    const updatedPeopleData = peopleData.map((person) =>
      person.id === selectedPersonID
        ? { ...person, todos: person.todos.filter((todo, i) => i !== taskID) }
        : person
    );
    setPeopleData(updatedPeopleData);
  }
And passing the whole peopleData into ShowTodosForm which meant that when that array was updated, it triggered the re-render. I also included the length of the todo list in the key. This meant they would change when the checkbox was ticked and not stay stale.

JavaScript code:
function ShowTodosForm({ peopleData, onTaskChecked, selectedPersonID }) {
  const person = peopleData.find((person) => person.id === selectedPersonID);
  return (
    <form className="form-show-todos">
      <h2>{person.name}&apos;s Todos</h2>
      <ul>
        {person.todos.length > 0
          ? person.todos.map((todo, i) => (
              <Todo
                key={`${person.id} - ${i}/${person.todos.length}`}
                taskIndex={i}
                todo={todo}
                onTaskChecked={onTaskChecked}
              />
            ))
          : 'Nothing to do'}
      </ul>
    </form>
  );
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply