Curso de Javascript Completo do iniciante ao mestre

Seção 2 - Revisão Lógica de Programação

Aula 12 / Aula 13 - Variáveis parte 1 / Variáveis parte 2

Introdução a variáveis. Tipos de variáveis em JS: var, let, const. Utilização de ' ou ". Tipagem fraca, ou seja pode mudar o tipo da variável.
O operador let, diferente do var não permite que a mesma variável seja inicializada duas vezes. Podemos reatribuir valores mas não reiniciar uma variável com let.
As variáveis tipo const não podem ser alteradas* e devem ter seu valor declarado no momento da inicialização diferente dos outros tipos.

Aula 14 a 17 - Tipos de dados primitivos / Number / Boolean / undefined e null

Tipos de dados primitivos em JS. Nomes de variáveis, utilização de 'escape (\)' para ignorar caracteres. Literal template com '` ${ } `'. Concatenação com '+'.
Utilização do typeof para saber o tipo de uma variável.
Variáveis tipo número (inteiro e float).
Váriaveis boolean (true / false).
Tipos undefined quando não há a atribuição de valor e null deve ser declarada explicitamente como 'null' e seu typeof retorna, por problema na estrutura do JS, o tipo object.

Aula 18 / 19 - Conversão entre tipos / Conversão entre tipos parte 2

Quando trabalhamos com variáveis de tipos diferentes, o Javascript pode fazer conversões implícitas para por exemplo executar uma operação aritmética entre dois números mesmo quando um deles é declarado como string sem mudar o tipo da string. Isso não funciona assim para a operação de adição pois nesse caso há uma concatenação.
É importante lembrar que toda a informação que vem da interface do usuário, mesmo que em um input do tipo número, chega no JS como string.
Apesar disso devemos tratar os valores com as funções parseInt, parseFloat e o método construtor Number().
O parseInt e parseFloat convertem strings em numeros, mesmo que a string tenha além de caracteres numéricos, outros caracteres. O único porém é que eles ignoram os valores que estiverem a partir do primeiro caractere não numérico. Dessa forma caso a string inicie com um caractere não numérico o retorno será NaN. Já o método construtor Number() exige que tenhamos apenas caracteres numéricos na string retornando NaN em todos os outros casos.
Já a conversão de numero para string pode ocorrer de duas formas. A primeira concatenando uma string vazia com o número e a segunda utilizando a função toString(). A função toString() admite por padrão que o número está na base 10, entretanto podemos alterar a base como parâmetro da função por exemplo toString(16)

Aula 20 a 26 - Operadores aritméticos / operadores de atribuição / incremento e decremento / operadores de comparação / operadores lógicos / precedência de operadores

Operadores aritméticos: ' + ', ' - ', ' * ', ' / ', ' % ', ' ** '
Operadores de atribuição: ' = ', ' += ', ' -= ', ' *= ', ' /= ', ' %= ', ' **= '
Incremento e Decremento: ' ++i ', ' --i ', ' i++ ', ' i-- ' (Atenção para os efeitos da posição dos sinais)
Operadores de comparação: ' == ', ' === ', ' > ', ' < ', ' >= ', ' <= ', ' != ', ' !== '
Operadores lógicos: ' && ', ' || ', ' ! '
Precedencia de operadores: ' && ' > ' || ', ' ** ' > ' / ' ' * ' > ' + ' ' - '

Aula 27 a 28 - Condicional If Else / Operador ternário

Utilização do condicional if / else, if / else if / else.
Condicional com operador ternário (condição ? true : false)

Aula 29 - Falsy e truthy

Alguns valores em JS são considerados por padrão como false. São eles: 0, "", NaN, undefined, null, false . Entretanto se fizermos um teste com uma string cujo valor é 'false' o resultado será true pois existe um valor (a string com o conteúdo 'false'). Todos os outros valores são considerados 'truthy' ou seja tendem a ser verdadeiros.

Aula 30 - Curto-circuito

Quando temos o valor de uma variável igual a '0' e queremos que substituir esse valor por 10, por exemplo, podemos efetuar a seguinte expressão:
var = var || 10

Ou seja caso o valor de 'var' retorne falso (no caso 0 é falsy como visto) ele será reatribuido para 10.
Outro tipo de curto-circuito é com o operador '&&'. Caso tenhamos um teste que retorna 'true' junto com o operador '&&' e uma outra função que não seja 'false' temos a execução da função. Por exemplo:
operação_true && console.log('é verdade')
Já se utilizamos o operador '||' caso o primeiro valor seja 'true' o segundo valor nem será executado. No caso do console.log() ele não apresentará nenhum resultado.

Aula 31 - Condicional switch

O switch é utilizado para testar valores específicos.
A sintaxe é:
switch(valor_teste){
case valor1:
operação
break
...
default:
operação
}

O teste é sempre por valor e tipo (===).

Aula 32 a 33 - Repetições / break vs continue

Utilização das estruturas de loop while, for e do / while
Utilização do break para interromper a execução e do continue para pular uma instrução. Atenção para a execução do continue em loops do tipo while pois ele pode interromper o incremento e entrar em um loop infinito.

Aula 34 a 36 - Funções / Funções que retornam valores / Funções que recebem parâmetros

Funções são blocos de código reaproveitável que são executadas quando solicitado.
Declaramos funções com a palavra reservada function seguida do nome da função.
Podemos declarar como function expressions que consiste em atribuir a função a uma variável. Nesse caso o nome da função é o nome da variável em que ela foi atribuida e a declaração da função é anonima.
E de modo mais recente temos as arrow functions que consiste em atribuir a função a uma variável como a function expressions, mas nesse caso não utilizamos o operador function mas apenas o '( ) => {}'.
O return em uma função encaminha o resultado do que foi executado pela função para quem fez a chamada da função. Desse modo se executarmos a função a partir da atribuição de uma variável a variável vai armazenar o valor encaminhado pelo return. No exemplo utilizamos o método Date().getDay() para retornar o dia da semana.
Podemos passar parametros para a função indicando na declaração da função quantos/quais parâmetros serão recebidos e passando esses parâmetros na chamada da função. No JS podemos passsar parâmetros a mais ou a menos que é executado do mesmo modo. O que pode ocorrer é que podemos ter resultados indesejados dependendo da operação que for realizada pela função. Podemos resolver isso passando parâmetros 'default' utilizando por exemplo 'curto-circuitos'.
Podemos, a partir do ES2015 passar os parâmetros 'default' simplesmente atribuindo o valor nos parâmetros na criação da função. O procedimento com 'curto-circuito' era utilizado nas versões anteriores.

Aula 37 a 39- Exercício proposto: mostrar tabuada / resolução: mostrar tabuada / resolução: limpar tabuada

Apresentar na tela a tabuada de acordo com o valor selecionado. Fiz a entrada de dados através de <input> no html, no exemplo foi feito com prompt.
Para a apresentação dos dados na tela, não lembrei como fazer de modo simples (apresentação diretamente com o innerHTML). Acabei utilizando &letable> com insertRow() e insertCell().
O modo correto de fazer é através de operador de atribuição com adição em uma variável e atribuir o valor da variável completa na tela.

Aula 40 - return dentro de loop

Atenção para o uso de return. Quando utilizamos o break ou o continue esses operadores de algum modo interrompem ou pulam um passo no loop, mas continuam a execução da função. Quando utilizamos o return a função para de ser executada, mesmo que tenhamos algo fora do loop.

Aula 41 - Escopo de variáveis

Quando utilizamos variáveis no escopo global ela pode ser manipulada em qualquer local de nosso script, inclusive dentro de blocos. Dessa forma se reatribuirmos o valor de uma variável global dentro de um bloco essa atribuição será refletida globalmente.
Entretanto caso declaremos uma nova variável com escopo em um bloco, mesmo que essa nova variável tenha o mesmo nome da variável global, essa atribuição ficará restrita ao bloco que ela pertence.
Quando utilizamos o var para declarar variáveis ela terá escopo de função, portanto pode ser acessada de qualquer lugar dentro da função, mesmo que esteja dentro de um outro bloco. Já quando utilizamos o let a variável fica restrita ao bloco em que ela foi declarada.

Aula 42 - Arrays

Arrays são conjunto de dados. Em JS os arrays permitem que coloquemos qualquer tipo de dado, inclusive outro array. Em JS não temos um tipo específico para arrays, elas são consideradas object
Podemos criar arrays utilizando a sintaxe formal que é como segue:
const array = new Array()

Para acessarmos o conteudo de uma array utilizamos o nome_da_array[indice]. Quando não temos valor no indice solicitado o retorno é undefined.
Para sabermos a quantidade de elementos em um array utilizamos a propriedade length. Para selecionarmos o ultimo indice de um array podemos utilizar nome_do_array.lenght - 1.
Podemos criar arrays também com a sintaxe literal da seguinte forma:
const array = []

Podemos acrescentar valores em um array utilizando o valor do próximo índice livre utilizando:
nome_do_array[nome_do_array.length] = novo_valor

Podemos também acrescentar valores em um array utilizando o método push

Aula 43 - Objetos

Objetos são conjuntos de dados compostos de chaves e valores. Assim como os arrays podemos criar objetos pela sintaxe formal:
const objeto = new Object()

Criado o objeto podemos incluir valores da seguinte forma:
nome_obj.nome_atributo = valor

Podemos passar qualquer conjunto de atributo / valor, independente de esse conjunto existir ou não no objeto.
Podemos consultar o valor dos atributos através do nome_do_objeto.nome_do_atributo ou com nome_do_objeto["nome_do_atributo"]. Com a segunda sintaxe podemos criar uma variável para indicar o nome_do_atributo.
Já na sintaxe literal inserimos os dados da seguinte forma:
const nome_do_objeto = { atributo_1: valor_atributo_1, atributo_2: valor_atributo_2, ... atributo_n: valor_atributo_n}

Aula 44 - Iterar Arrays

Podemos percorrer um array utilizando o loop for iterando cada índice do array.

Aula 45 - Iterar Objetos

Podemos iterar objetos utilizando o operador for / in.
Quando utilizamos esse operador a variável que criamos para o irá mapear os atributos como se fossem índices. Desse modo se acessarmos o objeto com essa variável como índice obteremos os valores dos atributos. O processe é como segue:
for (let variavel in objeto ) {
objeto[variavel]
}

Aula 46 - Métodos de Objetos

Métodos são funções dentro de objetos. Podemos criar métodos quando declaramos os objetos literais decalrando o nome do método e passando a função como valor ou declarando o método direto sem o operador function.
Podemos também criar o método utilizando o atributo e uma arrow function.
O operador this quando utilizado dentro do escopo do objeto se refere ao objeto como um todo.
Aqui temos uma observação. Quando criamos um método tanto simplificado como com o operador function temos acesso ao objeto com o operador this. Já quando utilizamos arrow function o retorno do ehis é um objeto vazio. Na verdade se verificarmos no browser estamos manipulando o window (esse é o retorno no console).

Aula 47 a 48 - Arrays de Objetos / Iterar array de objetos

Podemos criar arrays de objetos, utilizando a notação '[ { }, { }, ... , { } ]'.
Esses arrays são conjuntos de dados e permitem que se acesse os objetos pelo índice, e pelo atributo.

Aula 49 - Valor vs Referência

Em JS temos os primitivos que são objetosa imutáveis e que não tem métodos. Temos também objetos wrapper para os primitivos, ou seja, objetos que representam os primitivos. Para saber mais ver Mozilla Docs.
Quando analisamos duas variáveis diferentes com valores primitivos iguais, quando fazemos uma comparação recebemos como retorno true. Entretanto quando criamos duas variáveis atribuindo a elas arrays vazio temos como retorno false. Isso ocorre pois quando atribuimos um valor a uma variável ele ocupa determinado espaço na memória e define um valor específico. Já quando atribuimos um array vazio, estamos fazendo referencia ao array, que mesmo que tenha o mesmo conteúdo (no caso vazio) não é igual. Já se atribuirmos o valor de uma variável à outra variável teremos uma espelhando a outra, ou seja ambos os arrays apontam para o mesmo endereço na memória.
Quando executamos em uma função um método que manipula um array por exemplo, o método pode alterar o valor do array. Entretanto quando tentamos fazer o messmo com primitivos, não conseguimos fazer a alteração fora da função, assim sem que retornemos o valor para a variável externa a função não teremos alteração no valor da variável.

Aula 50 - Loop for vs loop while

Vamos criar um array utilizando o método Math.random().
Para verificar se o valor não será repetido no array utilizaremos o método indexOf() para testar os números existentes no array. Caso o número não seja repetido incluimos ele no array utilizando o método push
Esse exemplo é melhor executado com o loop while pois no loop for precisamos saber o número de repetições. O loop while permite que realizemos alguma ação até que determinada condição seja satisfeita.

Aula 51 a 52 - Tratamento de erros / finally

Quando precisamos passar mensagens de erro podemos enviar um alert ou outro tipo de mensagem, porém essas mensagens são proprietárias do navegador e não serão interpretadas em outros sistemas.
Para enviar mensagens de modo correto temos o comando throw Error(mensagem_de_erro). Essa mensagem será apresentada no console do navegador.
Para otimizar esse procedimento podemos utilizar a propriedade try / catch.
Com essa propriedade podemos testar a função que desejamos e retornar a mensagem de erro de diversas formas, independente do sistema utilizado.
Se quisermos apenas enviar a mensagem de erro por exemplo podemos utilizar a propriedade .message
Temos ainda o operador finally que pode ser incluido após o catch e que será executado de qualquer modo, em caso de execução correta ou em caso de erro.

Seção 3 - Sistema Léxico do Javascript

Aula 53 a 55 - Sistema léxico parte 1 / Sistema léxico parte 2 / Sistema léxico parte 3

Fundamentos do sistema léxico do JS.
Sintaxe, comentários, palavras reservadas, case sensitive, uso de ';' (obrigatório na mesma linha), nomes de variáveis e funções, camel case.
O use strict limita algumas ações indesejadas do JS como por exemplo, não permite a atribuição de variáveis não declaradas. O use strict DEVE ser declarado na primeira linha de código, caso haja alguma declaração antes o comando não funciona. O use strict pode também ser declarado no escopo de função, nesse caso ele é restrito ao escopo da função. A grande vantagem de utilizar o use strict é evitar problemas de escopo em variáveis. Outro problema resolvido é que o JS permite a declaração de parametros em funções com o mesmo nome. Nesse caso a função considera o segundo parâmetro passado e ignora o primeiro. Caso seja passado apenas o primeiro o resultado é 'NaN' ou 'undefined' (conforme o caso). Ele também evita problemas com o operador this em funções. Em funções o this se refere ao 'window' do navegador e não ao objeto. Dessa forma com o use strict o this em uma função retorna undefined.
A tipagem do JS é dinamica, ou seja ele pode alterar seu tipo simplesmente reatribuindo o valor a variável.
A aspas podem ser ' ou " devendo obedecer apenas a ordem de abertura e fechamento. Podemos escapar caracteres utilizando '\'.
O NaN ocorre quando fazemos uma operação matemática com caracteres não numéricos.
Por fim temos a conversão implícita no JS, ou seja, mesmo que voce passe um número como string se for possível o JS converte a string automaticamente para satisfazer a operação.

Seção 4 - Funções

Aula 56 - Introdução

Funções são objetos que tem a capacidade de ser invocados/executados.
Características: podem ser literais, ocorre o hoisting, são objetos ded primeira classe, são a única maneira de gerar escopo, podem ser auto-invocáveis (IIFE) podem ter propriedades internas como arguments e name.
Podem ser literais:
function nyFunc() { }
Hoisting:
Pode ser "içada" com function declaration. Quando declaramos uma função de forma literal podemos executar essa função antes da declaração da função. Entretanto quando atribuimos a função a uma variável ela só poderá ser executada após a declaração. O hoisting de variável também funciona. Se chamamos a variável antes de ela ser inicializada com var ela é içada, simplesmente não tem valor (undefined). Isso não funciona com variáveis iniciadas com const e let.

Aula 57 a 59 - Funções auto-invocáveis / Parâmetros para funções auto-invocáveis / use strict

Funções em JS são objetos de primeira classe. Podemos passar funções como parametro para outra função, podem ser atribuidas em propriedades de objetos (métodos), retornadas como resultado de outra função e ter suas próprias propriedades.
As funções auto-invocáveis(IIFE) servem para evitar poluir o escopo global.
Quando utilizamos mais de um arquivo de script nas aplicações, quando tinhamos a inicialização de variáveis por var caso tivessemos uma variável igual nos dois arquivos uma sobreescrevia a outra. Isso não ocorre com variáveis do tipo let. Entretanto isso ainda ocorre com funções.
Podemos isolar as funções em seus respectivos arquivos utilizando as funções auto-invocáveis. A sintaxe para isso é:
(function() {
//código a ser executado pelo arquivo, incluindo variáveis e funções
} ()


Com esse procedimento, isolamos o escopo de cada arquivo dentro dele mesmo, não sendo seu conteúdo acessável pelo escopo global. Dessa forma evitamos problemas como repetir funções em carquivos diferentes.
Hoje com os frameworks e outras ferramentas, não é mais necessário utilizar as IIFE pois os arquivos só se comunicam desde que haja o import entre eles.
Podemos passar parâmetros nas IIFE do mesmo modo que passamos para qualquer função, utilizando os últimos parenteses para passar os parâmetros e os primeiros para receber. Era comum passar como parâmetro para esse tipo de função os objetos globais window e document.
Para garantir mais ainda a consistencia dos escopos podemos utilizar juntamente com a IIFE o use strict. Com esse conjunto de sintaxe garantimos que todas as variáveis e funções estarão protegidas de utilização fora dos escopos.

Aula 60 a 62 - Arguments / Arguments vs arrow functions / Propriedade name

Quando precisamos criar uma função com numero de parâmetros indefinido (que pode variar conforme o caso) podemos resolver de várias formas. Uma delas é passar os valores em um array. O outro e mais correto é utilizar a propriedade arguments, que cria um pseudo array, com os dados passados no parâmetro. Essa propriedade permite também que se consulte o length dos dados passados.
O arguments funciona apenas para funções criadas com function expression ou function declaration. Não é possível utilizá-lo com arrow function.
Podemos consulatar também o nome da função utilizando a propriedade .name.

Aula 63 - Objetos de primeira classe

Podemos passar uma função como parâmetro para outra função. Isso é chamado função de callback. Nesse caso a função principal espera receber uma função para continuar sua execução. Podemos utilizar o teste de curto circuito para testar se o valor recebido é uma função para evitar quebrar a execução caso o parâmetro seja diferente do esperado.
Podemos passar a função como parâmetro diretamente na chamada da função principal através de uma função anonima, ou passar o nome de uma função do tipo declaration ou expression.
Podemos atribuir uma função como atributo em um objeto.
Também é possível passar uma função como retorno de outra função. Esse caso é um pouco mais complexo e tive um pouco de dificuldade de entender a lógica disso.
Como a função é considerada um objeto podemos incluir atributos ou propriedades.

Aula 64 a 65 - Desafio: calcular média / Resolução: calcular média

App para cáclulo de média com n parametros. A diferença da minha solução para a do professor foi o 'curto-circuito' utilizado para retorno caso os parametros forem em branco.

Aula 66 a 67 - Desafio: Calcular IMC / Classificação IMC

Sistema para cálculo de IMC. Fiz meu sistema recebendo e mandando dados para um página.

Aula 68 - Resolução: Calcular IMC

A diferença para o sistema desenvolvido pelo professor foi que eu fiz uma redundancia desnecessária no conjunto de teste lógico 'if'. Outra coisa é que eu não usei 'return' para os dados das funções. Fiz esse ajuste na minha resolução mas de outra forma.

Aula 69 - Callbacks

Funções de callback são funções passadas como parâmetro para outra função. Quando passamos uma função como parâmetro para outra não podemos executar essa função na função que a recebe (não podemos por o nome da função seguida de () no campo de parâmetros da função principal). Podemos passar parâmetros para a função de callback dentro da função que a executa.

Aula 70 - Resolução: Calcular IMC (com Callbacks)

Passando a função de descrição do grau de obesidade como 'callback' para a função de cálculo do IMC.

Seção 5 - DOM

Aula 71 - Introdução

DOM é o acronimo para Document Object Model. É uma API disponibilizada no browser para editar o que é mostrado na tela.
Para o JS os elementos criados em html formam um árvore hierárquica, com os elementos pai e filhos de acordo com sua estrutura.

Aula 72 a 74 - Selecionar elemento HTML no Javascript / Selecionar na árvore do DOM / Relembrar: onde inserir os nossos scripts

Podemos selecionar elementos do html utilizando o objeto document. Esse objeto tem alguns métodos para manipulação dos elementos:
getElementById(): seleciona o elemento pelo nome do id atribuido a ele.
querrySelector(): seleciona o primeiro elemento com o atributo selecionado (nesse caso devemos indicar o elemento como no CSS).
textContent: propriedade que altera o texto no objeto selecionado pelos métodos anteriores.
getElementsByClassName: cria uma collection que é como se fosse um array, com os elementos que tenham a classe indicada. Os elementos podem ser acessados pelo índice, como nos arrays.
querySelectorAll: funciona como o getElementsByClassName porém cria um NodeList ao invés de um collection.
getElementsByTagName: cria uma collection baseada na tag html escolhida.
Podemos navegar pela árvore do DOM criando variáveis com elementos específicos e utilizando essas variáveis para acessar elementos filhos.
Também é possível acessar os elementos utilizando o querySelector e selecionando o elemento através do caminho até o elemento mesclando id, class e tag como no css (utilizando a herança entre os elementos).
Quando manipulamos elementos no DOM não podemos incluir o script na tag <head> pois o browser vai tentar manipular a propriedade chamada por ele antes de carregar os elementos html.

Aula 75 a 77 - Exercício proposto: saudação / Resolução: calcular saudação / Inserir tags HTML

Manipular o html para inserir o nome 'dinamicamente' na saudação da página com JS.
Fizemos isso utilizando inicialmente a propriedade textContent com a concatenação(soma) por atribuição (+=).
Em seguida para passar o nome do usuário para negrito utilizamos a propriedade innerHTML para concatenar o valor junto com as tags <b>.
Lembrando que a propriedade textContent manipula apenas o conteudo da tag enquanto o innerHTML permite passar novas tags para serem interpretadas.

Aula 78 a 79 - Esconder elemento / Remover elemento

Manipulando as propriedades de style através do seletor em JS. Utilizando o parentElement, o parentNode e o children para navegar nos elementos HTML através dos seletores em JS.
Caso não queiramos ocultar o elemento com o display = 'none' temos a opção ded remover o elemento com o método remove() ou removeChild(). O método remove() não funciona em versões antigas de navegadores.
Esse modo tem um problema pois ao carregar a página até que o script seja carregado aparece a barra que deve ser removida por uma fração de segundos.

Aula 80 - Criar elemento

Poderíamos adicionar os elementos com o atributo innerHTML mas esse método pode gerar problemas. Para fazermos a inclusão de modo correto devemos incluir um node diretamente na árvore do DOM.
Vamos portanto criar um novo elemento utilizando o método createElement(tipo_de_elemento) atribuido a uma variável. A partir dessa variável podemos incluir um classe utilizando o atributo className e inserir o conteúdo dentro do elemento utilizando, aqui sim, o innerHTML.
Feito isso vamos selecionar a posição em que o novo node será inserido e executar o método que fará a inserção. O método que vamos utilizar é insertBefore() e possui a seguinte sintaxe:
elementoPai.insertBefore(novoElemento, elementoReferencia)

Para selecionar os elementos vamos navegar pelo DOM utilizando os métodos de seleção, no caso os métodos firstChild ou firstElementChild.
Atenção: o método firstChild considera qualquer elemento no HTML, inclusive comentários

Aula 81 - Simular o cadastro de e-mail

Foi feita uma alteração no documento HTML para incluir um <input> para inserir um e-mail e um <button> para enviar o valor.
Para isso vamos utilizar o atributo value do campo <input> e o evento onclick do <button>.
Como estamos lidando com funções chamadas pelo HTML (no caso pelo evento onclick) não podemos utilizar a função auto-invocável pois ela bloqueia o escopo do arquivo JS.
Outro ponto importante é sempre verificar o momento de utilizar a atribuição de valores a variáveis pois o valor de <input> só estará disponível se tiver alguma ação que faça sua leitura.

Aula 82 - Habilitar ou desabilitar um input

Manipulando os atributos disabled do elemento <input> do HTML utilizando o evento onclick e onblur, e utilizando o método focus().

Aula 83 a 85 - Propriedades / Desafio checkbox / Resolução checkbox

Utilizando propriedades HTML no JS.
Utilizamos as propriedades value, disabled, readOnly (readonly) (note que quando utilizamos propriedades HTML em JS utilizamos os nomes compostos em camel-case), htmlFor ('for' atributo de label), maxLength (maxlength), className
Para fazer o desafio do checkbox utilizamos a propriedade checked. Para monitorar o click no ckeckbox utilizei o evento onclick mas podemos utilizar também o evento onchange.
Como utilizamos duas propriedades com retorno true ou false podemos simplificar o teste lógico if que verifica se o checkbox está clicado e atribuir o resultado true ou false dele diretamemnte para a propriedade disabled. Essa simplificação fica como segue:
nome_botão.disabled = !checkbox.checked

O último detalhe nesse exercício é que o disabled do <button> de cadastro estava com a propriedade disabled inserida diretamente no HTML. O problema é que se houver algum problema com a carga do JS o usuário não terá como fazer nada. Para contornar essa situação passamos o controle da propriedade disabled para o JS, assim ele só ativará essa propriedade caso o JS seja carregado.

Seção 6 - Arrays

Aula 86 - every(), some(), forEach(), filter() e map()

Relembrando, podemos criar arrays de forma literal:
const arr = []

ou criando um objeto do tipo array:
const arr = new Array()

As funções every() e some() retornam um boolean.
A função every() requer uma função anonima como parametro. Esta função vai verificar todos os elementos do array e retornar true ou false se todos os elementos atenderem a condição especificada no corpo da função.
A função some() faz o mesmo que a função every() porém retorna true se apenas um elemento atender a condição solicitada pela função de parâmetro.
A função filter() não modifica o array original ele retorna um novo array. Ela deve receber uma função que pode receber três parâmetros (elemento, indice, array).
A função forEach() não retorna nenhum valor, ela apenas faz a iteração entre os elementos do array, portanto não adianta atribuirmos o valor de forEach() em variáveis. Apenas utilizamos ele dentro de uma lógica.
O método map() retorna uma nova array com os dados transformados. A sintaxe é a mesma dos demais recebendo os mesmos parâmetros. A diferença para o forEach() é que ele retorna os dados modificados.

Aula 87 - indexOf(), lastIndexOf(), includes(), find(), findIndex()

Primeiramente, o método find() não funciona em versões do Internet Explorer. Para saber quais implementações funcionam em navegadores podemos verificar o site Can I use.
Os métodos indexOf() e lastIndexOf() retornam o primeiro ou o útimo índice dos valores consultados. Caso o valor não seja encontrado o retorno será '-1'.
O método includes() retorna um booleano true ou false caso exista o valor pesquisado. Podemos substituir o includes() por um teste com indexOf() > -1 pois esta expressão retornará true ou false como no includes().
O método find() recebe uma fução como parâmetro e retorna o valor encontrado na função que satisfaça a condição. Caso não encontre retornará undefined.
o método findIndex() faz o mesmo que o find() mas retorna o índice. Caso não encontre valores retorna '-1'.

Aula 88 - concat(), join(), toString()

O método toString() converte o array em um string com seus valores separados por virgula.
O método join() faz o mesmo que o toString() porém aceita um parâmetro que define como serão separados os elementos do array.
O método concat() concatena arrays criando um novo array com os elementos. Podemos incluir novos elementos e mesmo novos arrays.
Quando apontamos um objeto não primitivo no JS estamos apontando para uma referencia dele na memória. Desse modo quando atribuimos o valor de uma array para outro array estamos apenas apontando o novo array para o endereço do array original. Dessa forma quando incluimos um dado no array que recebe o apontamento estamos incluindo os dados no array original. Sabendo disso, para criarmos realmente uma cópia do array e não simpelsmente um reapontamento do array na memória podemos atribuir ao array um valor vazio([ ]) e concatenar com o array que se deseja copiar da seguinte forma:
let novo_array = [].concat(array_original)

Aula 89 - push(), pop(), shift(), unshift(), slice(), splice()

O método push() insere dados ao final do array e retorna o 'length' do arquivo após a inclusão dos dados.
O método pop() remove o último elemento do array e retorna este elemento.
Note que tanto o push() quanto o pop() alteram o array original e não retorna o array modificado, ou seja não atribui o valor do array resultante a um novo array, ele apenas retorna os valores indicados e modifica o array original na memória. Portanto se precisamos manter o array original, ao utilizar esse métodos devemos fazer uma cópia desse array.
Caso seja necessário apenas recuperar o valor do ultimo item do array é mais indicado utilizar outra forma de consulta como pelo índice do último item.
O método shift() faz o mesmo que o método pop() mas manipula o primeiro item do array. Já o método unshift() faz o mesmo que o pop() porém inclui os valores no início do array.
O método slice(i_1, i_2) recorta o array nos valores de parametro indicados pelos indices. Ela não modifica o array original, apenas retorna os valores de indice1 até indice2(não inclusive). Podemos passar apenas o primeiro indice para recortar de indice1 até o fim do array.
O método splice() pode remover ou inserir itens no array. Ele modifica o array oruginal e retorna os elementos modificados. Caso passemos apenas o primeiro parametro ele remove todos os elementos a partir do indice informado e retorna todos os elementos removidos. Os outros parametros são o número de valores a serem removidos e os valores a serem incluidos. A sintaxe ficaria a seguinte:

arr.splice(indice_inicial, numero_valores_serem_removidos, valores_serem_inseridos)

Desse modo se selecionarmos o indice 'n' e passarmos '0' como valores_serem_removidos iremos incluir o valores_serem_inseridos a partir do indice sem remover nada.

Aula 90 a 91 - reverse(), reduce(), Desafio / resolução desafio

O método reverse() inverte a posição dos elementos no array. Ele modifica o array original e seu retorno é o novo array.
O método reduce() percorre o array e retorna um único valor. Ele recebe como parametro uma função com até 4 parametros:
function(acumulador, atual, indice, _arr){}

Em regra o acumulador recebe o primeiro valor do array e o atual o seguinte que é incrementado conforme as iterações. Podemos por exemplo fazer a soma dos valores do array passando para a função a soma do acumulador com o valor atual.
Podemos passar como parametro do reduce() (este parametro não é da função) o valor inicial do acumulador. Esse parametro será passado após a função. Note a sintaxe:

arr.reduce(function(acumulador, atual, indice, _arr){
lógica_da_função
}, valor_inicial_acumulador)


Se passarmos uma string vazia como valor inicial do acumulador o resultado será a concatenação dos valores do array (no caso do exemplo os valores do array são numeros, portanto concatenados com uma string tudo vira uma string). Podemos também passar como valor inicial do acumulador um array ou um objeto vazio. Neste caso os valores que serão iterados serão incluido no array ou objeto de acordo com a lógica da função. Para entender verificar a lógica utilizada no exemplo da aula (criar um objeto que indique quantos nomes existem no array com determinada letra).
No exemplo citado utilizamos o conceito de que uma string é um array de caracteres, portanto pegando o valor da string no indice '0' receberemos a primeira letra da string.
O desafio consiste em utilizar o reduce() para criar um array (passado como valor inicial do acumulador) com os números do array original, sem repetir os valores. Tentei de várias formas (utilizando forEach() e some()) mas não consegui resolver.
A solução foi basicamente a que eu pensei, mas o método a ser utilizado para verificar se já existe o valor no array de retorno é o indexOf(). Assim criamos o array com os valores do array original e testamos com 'if' se o indexOf() do valor atual não existe no array criado (se o retorno do indexOf() é menor que 0, ou seja, -1). Caso não exista inserimos o valor no array acumulador com o método push().

Aula 92 - Array.from() vs. Array.of()

Os métodos anteriores eram acessados pelo objeto array. Notamos issso pois utilizamos na notação a primeira letra do array minúscula(lambrando que isso é apenas notação). Já os métodos from() e of() acessam diretamente a função construtora (ou classe) do Array.
Os métodos Array.from() e Array.of() são incompatíveis com diversas versões de browsers mas possuem uma opção de compatibilidade que é a utilização do código polyfill, que é um código que emula a função em browsers incompatíveis. Mais informações em MDN Web Docs Array.from().
O método Array.from() cria um array a partir de outro array ou de um objeto que se parece com um array. Desse modo quando utilizamos o método Array.from(array_origem) criamos uma cópia do array de origem (??como o método é um método construtor ele cria um novo objeto e não apenas um novo apontamento de memória??).
O método Array.of() inclui um valor em um objeto do tipo Array. Quando passamos para um objeto do tipo Array apenas um valor numérico é criado um Array vazio com o número de posições indicados no valor do Array. Quando queremos criar o Array com apenas um elemento numérico precisamos utilizar o Array.of(numero).
Na aula foi demonstrado em seguida que é possível criar um NodeList de parágrafos com o método querrySelectorAll('p') e que esse NodeList se comporta como um array podendo inclusive receber métodos de array. No exemplo utilizaou-se o forEach() para perceorrer todos os elementos do NodeList e incluir em seu textContent o valor do índice para numerar os parágrafos.
Visto o exemplo acima, caso não possamos utilizar o forEach() caso tentemos utilizar o map() não será possível. Isso porque o método map() não é acessado pelo NodeList, pois o NodeList não é um array verdadeiro, ele apenas aparenta ser um array e aceita alguns de seus métodos. Para convertermos o NodeList utilizamos o método Array.from(nome_NodeList). Desta forma convertemos o NodeList em um Array verdadeiro sendo possível acessar os métodos de array.
Resumindo, utilizamos o método Array.from() para converter o NodeList (ou outros objetos que parecem array) em um array que permite a utilização dos métodos próprios de array.

Aula 93 - Spread operator

O spread operator 'espalha' os elementos de um array separando-os como se fossem valores separados. Seu operador é ....
Podemos utilizar o spread operator quando desejamos unir dois arrays com o método push(). Nesse caso se utilizarmos o push() diretamente no array teremos um array dentro do array principal. Para incluirmos os elementos do segundo array ao primeiro podemos utilizar o spread operator para espalhar os elementos do segundo array e inclui-los individualmente como elementos no array principal.

Aula 94 - Destructuring

O destructuring converte os elementos do array em variáveis isoladas de acordo com o número de elementos passados como parametro. Seu operador é o '[ ]' sendo que passamos dentro do operador o nome das variáveis resultantes. Podemos selecionar um elemento e utilizar o operador spread operator (...) para pegar os elementos restantes do array (neste caso o operador se chama rest operator). Podemos também pular elementos do array na atribuição das variáves bastando deixar o espaço daquele valor em branco. Identificamos o destructuring em um código observando a estrutura de array do lado esquerdo do operador de atribuição.

Aula 95 - for...of

O loop for..of funciona da mesma forma que o for...in entretanto o segundo pode ser utilizado em objetos e necessita que se declare a variável para o loop. Já o for...of faz a iteração apenas de arrays e não exige que se declare a variável de loop.

Aula 96 - Desafio

Desafio criar uma função de soma e uma função de média que podem receber n valores como parametro.
Fiz as funções inicialmente passando os valores em um array, mas não era isso o que devia ser feito e sim passar os valores como parametro na chamada da função.

Aula 97 - Resolução: Desafio

Na resolução do desafio eu fiz utilizando o método Array.from e o loop for...of Depois para fazer alguns testes, retornei os valores em uma array e utilizei o destructuring para apresntar o resultado.
O professor utilizou o spread operator e o método reduce().

Aula 98 - Introdução ao prototype, call() e apply()

Os métodos com prototype são úteis para programação utilizando a versão ES5, pois nesta versão não há suporte para métodos como Array.from(), spread operator e destructuring.
O prototype é o local onde se armazenam todos os métodos do método construtor Array. Desse modo, quando chamamos o construtor Array.prototype podemos chamar, a partir dele, qualquer método de array.
Já as funções podem ser executadas de três formas: chamando a função pelo nome seguida de '()', com ou sem parametros ou utilizando os métodos call() e apply(). Esses métodos quando utilizados alteram o escopo do this da função executada.
Para utilizarmos esses métodos utilizamos a sintaxe:

nome_da_funcao.call(null, parametros)

ou

nome_da_funcao.apply(null, [parametros])

A diferença como se nota é que no call passamos os parametros diretamente enquanto no apply passamos um array com os parâmetros.
Com base nessa informações, como o elemento arguments não é um array e sim um conjunto de elementos, não podemos utilizar os métodos nativos de array. Por isso utilizamos o prototype. Quando utilizamos a sintaxe Array.prototype.forEach.call(arguments, função_do_forEach) na verdade estamos "pegando emprestado" o método forEach() na biblioteca de funções de Array e aplicando ele em um objeto que não é um array (no caso a lista de dados arguments).
Essa abordagem serve para todos os métodos nativos de Array que precisemos utilizar em dados que não sejam arrays.

Seção 7 - Exercício proposto: Lista de alunos

Aula 99 - Introdução

O exercício é composto de um arquivo HTML com a tabela de dados a serem calculados, estilizado com o Materialize. Já temos as funções criadas na aula 97 para soma e média.
Para acessar os dados da tabela vamos recuperar primeiramente todas as linhas da tabela (<tr>) com o querrySelectorAll().
Em seguida para recuperar os dados da tabela vamos percorrer os valores da NodeList para selecionar cada <td>.
Nesse ponto pensei em criar um array de dados e remover o nome do aluno da primeira posição com o método shift() para em seguida utilizar o array para passar os dados para a função de média no js. A solução utilizada foi bem mais simples. Apenas passamos o valor como parametro para a função média selecionando cada valor pelo índice dele no NodeList e utilizando o atributo textContent.
Para não utilizar os valores dos índices fizemos a seleção através de propriedades criadas nas tags <th> no <thead>. Essas propriedades receberam nomes (n1, n2, n3, n4, media) e esses valores foram passados como atributos em um objeto literal cujos valores são os índices do array da tabela. Dessa forma podemos substituir os numeros dos indices por uma referencia a tag onde estão os valores.
Essa é apenas uma solução didática pois não tem fundamento utilizar isso na prática.
Uma observação: como criamos a variável com os índices fora da função auto-invocável (pois ela faz referencia a dados do DOM) precisei colocar um ponto e vírgula no fim do objeto pois ele estava causando um erro na função e o prettier do VSCode não estava colocando o ';' automaticamente.

Aula 100 - Melhoria: obter o índice de nossa coleção de objetos do DOM

O erro que eu observei no na criação do objeto foi comentado aqui. Esse erro é comum e por padrão quando se utiliza funções auto invocáveis iniciamos o código com o ';' antes da declaração da função para evitar que esse erro ocorra. O Prettier transfere esse ';' automaticamente para o objeto acima da função. De qualquer modo o objeto criado pode ser colocado dentro da função (diferente do que eu pensei não existe influencia do DOM).
Para melhorar a forma como recuperamos os índices nesse momento vamos criar uma função para recuperar os índices automaticamente, de acordo com o atributo criado em cada <th>. Note que mesmo quando criamos o objeto indicando o valor de cada atributo, esses valores continuam fixos.
Para fazer isso inicialmente referenciamos por variáveis os elementos do <thead> da tabela. Após isso vamos recuperar o indexOf() dos elementos necessários. O método indexOf() não é nativo do NodeList portanto devemos converter o NodeList para um objeto tipo array para podemos acessar seu indice. Podemos utilizar os tres modos aprendidos ( Array.prototype, Array.from ou ...[](spread operator)).

Aula 101 - Melhoria: gerar INDICES_NOTAS dinamicamente

Aqui vamos ajustar a forma como montamos o objeto de atributos e índices das <td> de dados. Para isso criamos uma variável que irá receber os elementos cuja propriedade contenha o valor [aluno-nota].
Criamos um array desse elemento com o método Array.from() e percorremos o array com o método forEach().
A função de callback do forEach() vai recuperar em cada <th> o valor de sua propriedade com o método getAttribute(). Esse método retorna o valor atribuido a propriedade.
Com esses dados populamos o objeto INDICE_NOTAS com os valores das propriedades (recuperados com o getAttribute) e o valor dos índices (recuperado com a função pegaIndice() criada na aula anterior).

Aula 102 - Gerar linhas e colunas a partir de uma estrutura de dados

Aqui vamos refazer a lista de alunos a partir de um array de objetos com os dados de cada aluno.
Inicialmente percorremos o array de objetos com o método forEach(), calculamos a média (com a função average()) e criamos um atributo media que será inserido nos objetos.
Em seguida vamos utilizar o método map() para mapear cada índice do array e retornar em cada elemento uma string contendo a estrutura HTML da <tr> de cada linha com os valores de cada coluna, tudo em um literal template. Um detalhe. Como o map() retorna os elementos do array como uma string separados por vírgula esse caractere aparece no documento HTML. Para resolver isso utilizamos o método join('') passando uma string vazia como separador entre os elementos.

Aula 103 a 104 - A estrutura de dados mudou. Precisamos refletir no DOM / Resolução: desafio

A mudança na estrutura de dados é que as notas agora estão em um array chamado 'notas' dentro do array de objetos 'alunos'.
A primeira forma, e mais fácil de resolver a mudança é chamar cada nota pelo número do índice dela no array.
A segunda forma é utilizar algum dos métodos de espalhamento de array, no caso o spread operator ou o apply.
Já para os valores que são inseridos no HTML inserimos no literal template um novo valor que agora fará o map() dos valores do array e irá inseri-los na tag <td>. Dessa forma o retorno do método map() será a string <td>valor_de_n</td>.

Aula 105 - Resolução: desafio 2

O segundo desafio consiste em definir cores diferentes de acordo com a média do aluno. Eu compliquei um pouco e criei, na minha resolução um novo atributo para o objeto que armazenava a cor de acordo com a média e refletia esse valor como um style inline no HTML.
A solução proposta foi criar classes para cada situação e utilizar o operador ternário diretamente na definição da classe para defini-la como 'aprovado' ou 'reprovado'.

Seção 8 - DOM - parte 2

Aula 107 - HTMLCollection vs NodeList

Aqui vamos entender as diferenças entre as NodeList e os HTMLCollection. Quando selecionamos os elementos com as propriedades do DOM temos resultados diferentes. O querrySelectorAll() retorna uma NodeList. Já o getElementsByTagName() ou getElementsByClassName() retornam uma HTMLCollection. A diferença entre o dois é que o NodeList é estático, ou seja, não é atualizado quando manipulamos o DOM, já o HTMLCollection é atualizado, mesmo quando limpamos o seu conteudo. Assim o NodeList reflete os valores do documento HTML original enquanto o HTMLCollection reflete as alterações feitas no DOM.

Aula 108 - Node vs Element

Há 12 tipos diferentes de 'nós' no DOM HTML (elemento, atributo, texto, comentário, etc). Um elemento é um tipo específico de nó. Desse modo quando nos referimos a tags estamos falando do nó elemento, assim como temos o nó dos atributos (class, href), o nó de text (para os textos das tags) o nó de comentários (por isso tive um problema com um método de chamada (parentNode) por nó que identificou o comentário).
Métodos de navegação entre nós:
Node.parentNode, Node.parentElement, Node.nextSibling, Node.previousSibling, Node.childNodes, Node.children, Node.firstChild, Node.firstElementChild, Node.lastChild, Node.lastElementChild e Node.hasChildNodes().
Métodos para adicionar nós:
ParentNode.prepend() e ParentNode.append(), Node.appendChild(), Node.insertBefore() e Node.cloneNode(), ChildNode.after() e ChildNode.before() e Element.insertAdjacentElement(), Element.insertAdjacentHTML() e Element.insertAdjacentText().
Métodos para remover nós:
Node.replaceChild() e Node.removeChild() e ChildNode.remove().
Métodos para criar nós:
document.createElement(), document.createAttribute(), document.createTextNode() e document.write().

Aula 109 a 111 - Navegar na árvore do DOM / children e childNodes / firstChild, lastChild

O parentElement e o parentNode são basicamente iguais e representam o elemento acima do elemento selecionado. As diferenças residem que em alguns casos o elemento não tem um elemento pai mas apenas um nó pai.
As propriedades nextSibling, nextElementSibling, previousSibling e previousElementSibling selecionam os nós ou elementos irmãos anteriores ou posteriores.
A propriedade childNode recupera todos os nós filhos do elemento seletor(em NodeList), já o children recupera apenas os 'elementos' filhos do seletor (em HTMLCollection).
As propriedades lastChild, firstChild recuperam os últimos e primeiros filhos do seletor. Já o lastElementChild e firstElementChild recuperam o último e primeiro elementos filhos do seletor.
O método hasChildNodes() verifica se o seletor tem ou não nós filhos. Retorna 'true' mesmo que haja apenas um espaço dentro do nó verificado.

Aula 112 a 116 - Create DOM / Adicionar DOM / after e before / insert / remove

Podemos inserir elementos no DOM com o innerHTML, entretanto em algumas situações é ncessário utilizar os métodos de criação do DOM.
Para criar elementos utilizando os métodods do DOM podemos utilizar os métodos: createElement('nome_do_elemento') para criar as tags.
createAttribute('nome_do_atributo') para criar atributos (como id, class, style). Feito isso passamos o valor do atributo com variável_do_atributo.value e setamos o atributo no elemento com setAtributeNode().
createTextNode('texto') cria o conteudo de texto do elemento. Utilizamos o appendChild()para inserir o texto no elemento.
Podemos criar atributos diretamente com o setAtribute('nome_do_atributo', 'valor_do_atributo') e inserir textos com textContent, sendo esses os métodos mais utilizados.
Para inserir o conteudo criado no documento podemos utilizar o método document.querySelector(elemento_pai).appendChild(elemento_criado)
Podemos inserir os elementos também utilizando os métodos prepend() que adiciona o elemento antes do elemento selecionado ou append() que insere no final do elemento selecionado (esses métodos não funcionam no IE). Com esses métodos podemos incluir mais de um elemento no DOM, inclusive uma string sem formatação.
Outra forma é utizando o método insertBefore(valor_a_inserir, elemento_de_referencia).
Os elementos inseridos com os métodos acima, caso sejam indicados para um nova posição são reescritos. Para copiar esses elementos utilizamos o método cloneNode(). O cloneNode() clona apenas o elemento selecionado. Para inserir todos os filhos desse elemento devemos passar o parametro true.
Os métodos after() e before() também inserem elementos depois ou antes do elemento selecionado. Eles também são incompatíveis com IE.
Os métodos de insert podem ser insertAdjacentHTML(), insertAdjacentText() e insertAdjacentElement(). Todos eles recebem um primeiro parametro que poded ser beforebegin, afterbegin, beforeend e afterend.
A diferença dos três é no segundo parâmetro. O primeiro aceita strings com código HTML, o segundo apenas textos enquanto o terceiro exige a criação de um nó através do createElement().
Os útimos métodos são para remover elementos. Para isso temos o remove(), o removeChild() que dede ser invocado pelo parentNode e o replaceChild() que recebe como parametro (novo_elemento, velho_elemento).

Aula 117 a 118 - Desafio / Resolução: desafio

O desafio consiste em movimentar algumas tags de acordo com algumas instruções.
Na resolução tive duas divergencias. A primeira foi que inseri os elementos clonados na <ul> quando deveria ter inserido na <li>. Com isso prcesamos manipular o elemento filho do filho. A solução para isso é remover o elemento diretamente no clone, pois ele continua sendo apontado como clone refletindo no documento.
A segunda é que fiz um loop para remover as <li> e o professor fez apenas uma limpeza do conteúdo passando um valor '' com o innerHTML

Seção 9 - Eventos

Aula 119 - Introdução

Evento é uma ocorrecia interativa que ocorre por intervenção do usuário ou não. Event handlers é uma função que é executada quando um evento ocorre. Disparador de eventos é o elemento que diparou o evento ou seja o elemento ao qual foi atrelado o event handler.
Quando utilizamos o DOM - Level 0 utilizamos os eventos inline do tipo onclick. Já para o DOM - Level 2 que é a mais moderna vamos utilizar o método addEventListener(evento, função, fase).
Uma das desvantagens de utilizar o DOM - Level 0 é que não podemos passar duas funções para o mesmo evento de um elemento.
Quando executamos uma função disparada por um evento, tanto com onclick quanto com addEventListener não devemos passar os '( )' da função (se o evento estiver sendo executado no script).
Note pelos exemplos que podemos colocar vários eventos quando utilizamos a notação de DOM Level 2, e que podemos utilizar ainda o DOM Level 0 junto, mas apenas as notações de DOM Level 2 permitem mais de uma interação.

Aula 120 - Propagação de Eventos

O terceiro parametro do addEventListener() á a fase que pode ser a fase de captura ou a fase de borbulhamento (bubbling) que é a padrão. Caso setemos o parametro como true estamos selecionando a fase de captura. Essas a fase de bubbling ocorre quando o elemento é executado de baixo para cima na arvore do document. Já a fase de captura ocorre de modo inverso.

Aula 121 - O objeto event

Podemos acessar o objeto event passando um parametro para a função de callback do evento que queremos manipular. Esse objeto possui diversos atributos do evento, entre eles o stopPropagation() esse método para a propagação do evento.

Aula 122 - Delegação de eventos

Utilizamos a delegação de eventos quando precisamos passar o mesmo evento para vários elementos que estejam na mesma estrutura. Nesse caso aplicamos o evento ao elemento pai delgando esse evento aos filhos.
Para aplicar a delegação primeiramente selecionamos todos os elementos filhos com querrySelectorAll(). O resultado é uma NodeList portanto não podemos passar métodos diretamente para eles.
Para passar os listeners para os elementos podemos utilizar cada índice do NodeList ou criar um loop com o spread operator e o método forEach() aplicando o listener em cada elemento do NodeList. Entretanto esses modos são muito custosos em memória para o usuário pois cria diversos listeners (um para cada elemento).
Portanto para fazer a delegação criamos o addEventListener() no objeto pai (geralmente o container dos elementos). Feito isso podemos acessar pela função de callback do addEventListener() com o event object a propriedade target que mostra qual elemento foi manipulado. O problema é que nesse caso todo o container responderá ao listener, ou seja, mesmo as áreas externas aos elementos tratados. Para contornar esse problema podemos fazer um condicional para só aceitar a propagação caso o elemento seja do tipo desejado.

Aula 123 - target vs. currentTarget

Quando utilizamos o atributo target temos como resposta o elemento clicado. Já quando selecionamos o currentTarget temos como retorno o elemento que chama o método addEventListener()
Nesse contexto, quando utilizamos funções do tipo function expression o this se refere ao currentTarget. Já quando utilizamos arrow function o this deixa de se referir ao currentTarget e passa a se referir ao objeto global ou seja o Window

Aula 124 - Exercício proposto: validar formulário

Habilitar o botão de envio apenas quando o checkbox estiver clicado. Enviar uma mensagem caso o Título não esteja preenchido.

Aula 125 - Cancelar envio formulário

O click do botão (tipo <input type='submit'>) que eu fiz está correto. Umas das coisas que foram ajustadas é que o 'submit' tem por padrão enviar o formulário, mesmo que não tenha sido validado. Para evitar esse comportamento devemos configurar no event object o atributo preventDefault() para evitar o comportamento padrão. Outro atributo que foi incluido é o focus() para o <input> do título caso ele passe a mensagem de campo não preechido.
Apesar disso tudo, o correto é monitorar não o <input type='submit'> ou seja o botão de envio, mas sim o evento submit do <form> portanto mudamos a abordagem para monitorar com o addEventListener() o evento 'submit'.

Aula 126 - Exercício proposto: contador de caracteres

Temos o contador de caracteres sendo exibido no formulário, entretanto caso o JS não funcione ele não terá nenhuma utilidade, portanto deixamos ele com display= none até que o script seja executado.
Criamos as variáveis de acesso aos elementos e manipulamos o display do elemento. Em seguida vamos acessar o <span> que contém o contador. Como apontamos para ele com o getElementsByTagName() teremos uma HTMLCollection (mesmo tendo apenas um elemento do tipo span) então precisamos acessar o índice [0].
Para definir o número de caracteres vamos recuperar o valor do maxLegth definido no <textarea> do HTML.
Em seguida vamos definir qual evento será monitorado pelo contador no <textarea>. Podemos utilizar os eventos keyup, keydown, change input, e keypress. O evento change, não responde a mundanças por digitação, portanto pode ser descartado. O keyup não vai funcionar caso a tecla fique pressionada por mais tempo (pois nesse caso conta-se os outros eventos). Já os eventos keypress e keydown não monitoram ações de copiar e colar. Portanto o evento mais confiável é o input. Com isso podemos recuperar a quantidade de caracteres digitados verificando o número de letras digitadas através do value.length do valor recebido no <textarea>. O valor a ser exibido no <span> do contador é a diferença do valor máximo (definido pelo maxlength) e o valor recebido. Recuperamos ess valor com o operador this que é o currentTarget da função. Essa função pode inclusive ser criada fora do listener e executada conforme necessário.

Aula 127 a 128 - Melhoria: evitar números 'mágicos' / exercício proposto: aceitar os termos para usar o form

O número de caracteres disponíveis inicial estava fixo na <span> que o armazena. Vamos agora tornar esse valor dinamico para que ele mude caso mudemos o maxlength do <textarea>. Para isso atribuimos o valor da variável que retem o maxlength ao apontador da <span>. Depois foi criada uma função para fazer esse processo.
Para a verificação do checkbox eu havia utilizado um if para verificar se o checked era 'true' ou 'false'. Entretanto podemos fazer isso atribuindo diretamente o valor a propriedade disabled do botão, utilizando a negação/inverso (!) da propriedade checked do input check.

Aula 129 a 130 - Exercício proposto: Feedback message / executar callback quando fechar

Para mostrar a mensagem de erro quando não tiver título digitado, precisamos acrescentar uma classe show na <div> que encapsula a mensagem de erro. Essa classe está no CSS da página e muda a propriedade transform: translateY para apresentar o modal na tela. Se utilizarmos as propriedades setAtribute, ou className vamos sobreescrever as classes que por ventura tenham sido definidas para o elemento. Para evitar isso utilizamos o método classList.add() que adiciona uma classe a outras se existirem. Criamos também uma função para remover a classe show quando o botão de fechar do modal for clicado utilizando o addEventListener() no clique do botão. Em seguida vamos alterar o focus() do título para que ele ocorra apenas quando cliquemos no botão de fechar o modal. Para isso criamos uma função de callback que será executada como parâmetro da função que exibe o modal. Para isso passamos a função de fechar o modal para dentro da função que exibe o modal, pois um só vai existir se o primeiro for executado.
Ao passarmos a função de fechar o modal para a função de erro temos um novo problema. A cada vez que clicamos no botão fechar um novo listener é adicionado. Podemos solucionar isso utilizando o método removeEventListener, porém ele não é executado quando passamos funções anonimas. Criamos portanto uma função nominada para fechar o modal de erro e incluimos nela o removeEventListener().
A chamada da função de callback que executa o focus() ficará dentro da função de fechamento do modal e executará quando o botão for clicado.

Aula 131 - Acessibilidade: Fechar com o ESC do teclado

Aqui criamos um listener para monitorar o teclado quando o modal estiver aberto. Caso a tecla ESC seja pressionada, monitoramos o evento keyup para executar a função de fechamento do modal.

Aula 132 - Eventos de teclado

Entender os eventos de teclado keyup, keydown e keypress. O keydown e o keypress só apresentam o valor digitado após registrar o evento portanto só captura o valor digitado após ser disparado, já o keyup, como depende de que a tecla seja liberada apresenta o valor quando dispara o evento. O keypress por sua vez não detecta teclas especiais como shift, ctrl e esc

Seção 10 - Exercício proposto: lista de tarefas

Aula 133 a 135 - Introdução / Adicionar li / Melhoria de usabilidade

Apresentação do exercício: criar uma lista de tarefas. Vai ser utilizado neste projeto o Local Storage do browser.
O primeiro passo realisado foi a inclusão das <li> utilizando o método mais simples: o método innerHTML com literal template.
As melhorias foram limpar o conteudo do input após a inclusão e manter o foco no campo.

Aula 136 a 137 - Criar tarefa em perder o listener / Melhoria: reaproveitar código

Nesta aula vamos criar uma função para poder recuperar as <li> inseridas ao clicar no item na tela. Para isso precisamos criar um addEventListener monitorando os itens da tabela. Para fazer issso vamos utilizar o spread operator em uma variável que irá conter a HTMLCollection das <li> e aplicar com o método forEach() o listener em cada uma. O problema é que quando utilizamos o innerHTML += a cada elemento inserido limpamos o conteúdo e reinserimos os novos dados. Portanto nesse caso deveremos utilizar os eventos de createElement, e appendChild. Para tanto criamos uma função que cria a estrutura que necessitamos e passamos essa função para o listener do submit. O problema ainda persiste pois não é adicionado o 'listener' das <li> pelo spread operator. Temporariamente vamos adicionar os listeners na função que cria as <li>.
Vamos agora entender as propriedades de texto:
as propriedades textContent e innerText retornam o texto do elemento. A diferença é que o textContent recuperamos parte das quebras de linha e espaços enquanto o innerText não mostra essas formatações. A propriedade innerHTML recupera a <tag> em que o elemento está contido. Já a propriedade outerHTML mostra o elemento anterior ao elemento em que ele está encapsulado.
Fizemos uma pequena refatoração para incluir uma função que, nesse momento, apenas encapsula os console.log() das <li>.

Aula 138 - Consumir uma estrutura de dados

Vamos a partir daqui remover todas as <li> do HTML para apenas inseri-las dinamicamente. Para isso vamos trabalhar com estrutura de dados. Essa estrutura de dados será um array de objetos com tres atributos definidos.
Criada a array de objetos a função que adicionava os valores em <li> no HTML passará a incluir os objetos na array com o método push() e substituindo o valor do atributo name do objeto pela task digitada (passada por parâmetro pela função).
Criamos uma nova função para criar a estrutura das <li> que irá receber o objeto e incluir na li o valor do atributo objeto.name. Essa função irá retornar a <li> montada que será renderizada a cada inclusão pela função renderTasks() que limpa o conteudo da <ul> do HTML, e reinsere todas as <li> através da leitura do array de objetos com o método forEach().

Aula 139 a 140 - Incrementar DOM / criar editContainer

Criação dos outros elementos internos das <li> do HTML através da inserção das tags na construção da string.
Criação do container para a edição das tasks.

Aula 141 a 142 - Aplicar delegação de eventos / Uso de atributo no HTML

Aqui vamos começar a criar os eventos para os pontos de click das <li>. Como temos muitos itens vamos utilizar a delegação de eventos.
Criamos para isso atributos para cada elemento clicável de modo que eles possam ser identificados individualmente. Poderíamos ter utilizado outros modos de selecionar esses elementos como class ou id. O problema de utilizar classes é que caso posteriormente utilizemos novas classes no elemento o retorno do evento e.target.className também mudará e precisaremos ajustar o listener. Uma opção é utilizar o e.target.classList.contains('nome_da_classe') mas isso também deixa o nome da classe engessada, muito embora em não veja mutios problemas, apesar que nesse caso específico a classe se refere a um ícone (classe do font awesome) e podemos querer mudar esse ícone.
Criamos os atributos pois eles não serão utilizados para mais nada no código.
Definidos os atributos podemos fazer testes lógicos para identificar qual elemento foi clicado. Uma opção é utilizar if...else if, outra é utilizar o switch.

Aula 143 - Obter a tarefa clicada

Ainda não criamos o resto do objeto de 'data-action' mas aqui vamos começar a identificar qual <li> está sendo manipulada. Para isso montamos uma lógica com while verificando qual o nodeName do elemento. Caso o ele não seja uma <li> buscamos o parentElement, ou seja, subimos a árvore de elementos do DOM até chegar na <li>.
Em seguida vamos utilizar o getElementsByTagName para recuperar as <li>. Importante lembrar que o getElementsByTagName retorna um HTMLCollection que é dinamica (diferente da NodeList), ou seja, é atualizado conforme vão se inserindo novos elementos. Se utilizassemos o querrySelectorAll não receberíamos nenhum valor pois ele retorna uma NodeList.
Uma vez que temos acesso a HTMLCollection de <li> recuperamos o índice do item clicado utilizando um spread operator na HTMLCollection e buscando o indexOf() do elemento clicado. Esse valor será utilizado em todos os métodos daqui para frente.

Aula 144 - Deletar a tarefa

Para remover os elementos com o clique no delete pensei em utilizar o método remove na lista de <li> porém isso não funciona pois devemos remover da coleção criada no array de objetos.
Como é um array utilizamos o método splice() passando como parametro o índice do elemento a ser removido e '1' que é o número de elementos a ser removido. Feito isso só precisamos renderizar novamente a <ul> para remontar a lista. Nesse momento poríamos utilizar o remove() para remover apenas a <li> excluida, porém é mais seguro remontar toda a lista para que tenhamos certeza de que o valor apresentado continua sendo um reflexo do conjunto de dados, mesmo que esse método exija um pouco mais de processamento pois a cada evento remonta toda a estrutura do HTML.

Aula 145 a 147 - Editar tarefa parte 1 / Editar tarefa parte 2 / Cancelar edição

Para editar a tarefa, devemos abrir o modal de edição. Faemos isso alterando o display: none no container do modal. Para isso selecionamos a classe dentro do <li> selecionado e alteramos a propriedade display para display: flex.
Com essa alteração exibiremos o modal, porém como ela insere um estilo inline quando clicamos em outro botão de edit abiremos um novo modal sem fechar o anterior. Para resolver isso foi feito um spread operator na <ul> para utilizar um forEach() que remove todos os estilos inline das <li>, para depois inserir o display: flex apenas na <li> selecionada.
Para gravar o valor digitado no modal de edição na lista selecionamos o campo de input do modal e recuperamos o seu value. Esse valor deve ser inserido no atributo name do objeto no índice selcionado. Uma nova questão surgiu e foi feito o auto-complete do input do modal com o valor atual da tarefa. Eu fiz isso diretamente na função executada pelo botão edit, mas o professor inseriu diretamente na criação do modal na função que gera as tags.

Aula 148 - Tarefa cumprida

Para fazer o marcador de tarefa completa ou não, primeiramente precisamos inserir um atributo data-action na tag <i> que contém a classe do ícone de check (esse ícone faz parte da biblioteca do font awesome e controla o tick através de uma classe display). Essa classe ('displayNone') que determina o display ou não do tick.
O listener do checkButton monitora um atributo do array de objetos (completed: true or false).
Com isso monitoramos o atributo data-action e alteramos a classe displayNone da tag na função que constroi a tabela.

Aula 149 a 150 - Armazenar em LocalStorage / O que acontece quando o usuário limpa o LocalStorage?

Como vamos começar a incluir dados no LocalStorage vamos mudar o valor atribuido para o 'arrTasks' para uma função que irá recuperar os dados.
Esses dados devem ser armazenados como JSON ou seja uma string de dados em formato JSON.
Na função que irá recuperar o array para alimentar o HTML vamos recuperar os dados do LocalStorage com o método localStorage.getItem('nome_biblioteca_no_LocalStorage'). Esses dados devem ser tratados com o JSON.parse() para converte-los em array. Caso não tenhamos dados no LocalStorage, fazemos um teste com um operador ternário em que caso não tenha nada crie uma lista padrão.
Para finalizar precisamos de uma função para armazenar os dados no LocalStorage, que irá executar o método localStorage.setItem('nome_biblioteca_no_LocalStorage', valor_a_adicionar), sendo que o valor a adicionar deve ser convertido para string JSON com o método JSON.stringify
Entretanto quando limpamos o LocalStorage o return da função que recupera os dados resulta em erro pois não testamos se existe o objeto no LocalStorage. Para resolver isso fiz um teste lógico que verifica se existe a chave dos dados armazenados e caso contrário cria a chave.
O professor criou apenas um 'curto-circuito' no próprio return testando tasksData && tasksData.length no operador ternário, assim caso o tasksData tem que existir 'e' o tasksData.length deve ser diferente de false.