how to use module factory to create objects and manipulate the dom in javascript?

I have this html code

<label for="AccountName">Account Name:</label>
        <input type="text" id="mytext"  >
        <label for="Deposit">Deposit: </label>
        <input type="text" class="deposit" id="deposit">
        <button type="submit" id="createAccount" onclick="createAccount()">Create new Account</button>
        <textarea name="mytextarea" id="mytextarea" cols="140" rows="20"></textarea> 

that I want to get values from the input fields and construct account object from it and add objects to accountInfoList which is a list. And finally I want to update the content of mytextarea with the accountInfoList. Sample run looks like this
sample output

The javascript code I wrote is

    var  accountInfoList = ();

const makeCounter = function(){
    let name = document.getElementById('mytext').value;
    let balance = document.getElementById('deposit').value;

    function createAccount(AccountName, AccountBalance){
        this.AccountName = AccountName;
        this.AccountBalance = AccountBalance;
    }

    var account = createAccount(name, balance);

    accountInfoList.push(account);

}

// document.getElementById('createAccount').onclick = createAccount;

function createAccount(){
    makeCounter();
    // console.log(account1);
    // const account2 = makeCounter(a)
    // accountInfoList.push(account1);
    console.log(typeof accountInfoList)
    for(var i = 0; i < accountInfoList.length; i++){
        console.log("Account Name: "  + accountInfoList(i).AccountName + "Balance: " + accountInfoList(i).balance);
    }
    // document.getElementById('mytextarea').value = accountInfoList(0).name;

}

which doesn’t seem to be working. Any idea is appreciated.

Custom ACF block only outputs commented JSON to the DOM

I created several custom ACF blocks and all but one are working properly. This one in particular only outputs commented out JSON to the DOM.

Here’s what it’s displaying (with a few string values removed to keep the client anonymous).

<!-- wp:acf/button {
    "id": "block_60dba85ef281c",
    "name": "acf/button",
    "data": {
        "button_background": "purple",
        "_button_background": "field_6061da93a6d15",
        "button_text": "",
        "_button_text": "field_606227d36073e",
        "button_link_target": {
            "title": "",
            "url": "https:///",
            "target": ""
        },
        "_button_link_target": "field_6061dc177bf40",
        "open_in_new_tab": "0",
        "_open_in_new_tab": "field_6061dc7d1fa44",
        "block_background": "purple",
        "_block_background": "field_606a59f7086ae",
        "button_alignment": "center",
        "_button_alignment": "field_606a5a8a98021"
    },
    "align": "",
    "mode": "preview"
} /-->

I registered this block in register_acf_block_types() the same way I registered all the other blocks and verified the template file exists and doesn’t have any errors. It just displays a simple styled anchor tag.

One thing I noticed is the "mode": "preview" line at the end. But this block has been published.

I’d appreciate any insight on this. Not quite sure where to look at this point.

javascript – Using OOP objects mapping to DOM elements vs. querying the DOM repeatedly?

I’m trying to make better use of OOP, but I don’t have good instincts for this yet and I sometimes worry I’m making things more complicated and/or less efficient. I’m wondering what is the best way to handle this situation working with groups of related objects that essentially map to DOM elements…

I’ve written a class to represent expanding elements (like dropdown/mobile menus, Q&As, etc.) which takes some arguments to set up, and has methods to expand and collapse. Here’s a simplified version for the purposes of this question (methods here just add/remove CSS classes to handle transitions, but there are more things I’m doing in my actual code with animations, keyboard input, etc. that seem worth building something modular and reusable):

class Expandable {

  constructor({ toggleButton, toggleButtonClass = "toggle-expand", expandableClass = "expandable" }) { 
    this._toggleButton = toggleButton;
    this._toggleButtonClass = toggleButtonClass;
    this._expandableClass = expandableClass; 

    this._expandableId = this._toggleButton.getAttribute('aria-controls');
    this._expandable = document.querySelector(`#${this._expandableId}`);
    this._expandable.classList.add(expandableClass);

    this._setEventListeners();
  }

  _setEventListeners() {
    this._toggleButton.addEventListener('click', (e) => {
      this._handleClick();
    });
  }

  _handleClick() {
    if(this._toggleButton.getAttribute('aria-expanded')  === 'true') this.collapse();
    else this.expand();
  } 

  expand() {
    this._toggleButton.setAttribute('aria-expanded', 'true');
    this._expandable.classList.add(`${this._expandableClass}_open`);
    this._toggleButton.classList.add(`${this._toggleButtonClass}_on`);
  }

  collapse() {
    this._toggleButton.setAttribute('aria-expanded', 'false');
    this._expandable.classList.remove(`${this._expandableClass}_open`);
    this._toggleButton.classList.remove(`${this._toggleButtonClass}_on`);
  }
}

Now I’m trying to create a class to represent a group of related Expandable objects, with a method to close everything at once. So far I have:

class ExpandableGroup {

  constructor({ toggleButtonClass = "toggle-expand", expandableClass = "expandable" }) { 
    this._buttonSet = document.querySelectorAll(`.${toggleButtonClass}`);
    this._expandables = ();

    this._buttonSet.forEach((button) => {
      this._expandables.push( 
        new Expandable({ toggleButton: button, toggleButtonClass, expandableClass })
      );
    });
  }

  closeAll() {
    // ???
  }

}

What is the best (or a non-terrible) way to implement the closeAll method using an OOP approach? I used to do this (without OOP Classes, just a mess of different functions that were redundant and hyperspecific) by using querySelectorAll() with my “open” class to get all the “open” elements, then loop through and remove that class from each. One thought I had is to have an isOpen property of the Expandable class, update it on expand/collapse, then the closeAll method of the ExpandableGroup could filter the _expandables array for any Expandable objects that have isOpen === true, and then with the results run their collapse method to close them. But is that a good approach? And how does it compare in terms of efficiency to the “old way” where I’m querying the DOM vs. filtering an array of objects?

javascript – Why does jQuery or a DOM method such as getElementById not find the element?

Short and simple: Because the elements you are looking for do not exist in the document (yet).


For the remainder of this answer I will use getElementById as example, but the same applies to getElementsByTagName, querySelector and any other DOM method that selects elements.

Possible Reasons

There are two reasons why an element might not exist:

  1. An element with the passed ID really does not exist in the document. You should double check that the ID you pass to getElementById really matches an ID of an existing element in the (generated) HTML and that you have not misspelled the ID (IDs are case-sensitive!).

    Incidentally, in the majority of contemporary browsers, which implement querySelector() and querySelectorAll() methods, CSS-style notation is used to retrieve an element by its id, for example: document.querySelector('#elementID'), as opposed to the method by which an element is retrieved by its id under document.getElementById('elementID'); in the first the # character is essential, in the second it would lead to the element not being retrieved.

  2. The element does not exist at the moment you call getElementById.

The latter case is quite common. Browsers parse and process the HTML from top to bottom. That means that any call to a DOM element which occurs before that DOM element appears in the HTML, will fail.

Consider the following example:

<script>
    var element = document.getElementById('my_element');
</script>

<div id="my_element"></div>

The div appears after the script. At the moment the script is executed, the element does not exist yet and getElementById will return null.

jQuery

The same applies to all selectors with jQuery. jQuery won’t find elements if you misspelled your selector or you are trying to select them before they actually exist.

An added twist is when jQuery is not found because you have loaded the script without protocol and are running from file system:

<script src="https://somecdn.somewhere.com/jquery.min.js"></script>

this syntax is used to allow the script to load via HTTPS on a page with protocol https:// and to load the HTTP version on a page with protocol http://

It has the unfortunate side effect of attempting and failing to load file://somecdn.somewhere.com...


Solutions

Before you make a call to getElementById (or any DOM method for that matter), make sure the elements you want to access exist, i.e. the DOM is loaded.

This can be ensured by simply putting your JavaScript after the corresponding DOM element

<div id="my_element"></div>

<script>
    var element = document.getElementById('my_element');
</script>

in which case you can also put the code just before the closing body tag (</body>) (all DOM elements will be available at the time the script is executed).

Other solutions include listening to the load (MDN) or DOMContentLoaded (MDN) events. In these cases it does not matter where in the document you place the JavaScript code, you just have to remember to put all DOM processing code in the event handlers.

Example:

window.onload = function() {
    // process DOM elements here
};

// or

// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
    // process DOM elements here
});

Please see the articles at quirksmode.org for more information regarding event handling and browser differences.

jQuery

First make sure that jQuery is loaded properly. Use the browser’s developer tools to find out whether the jQuery file was found and correct the URL if it wasn’t (e.g. add the http: or https: scheme at the beginning, adjust the path, etc.)

Listening to the load/DOMContentLoaded events is exactly what jQuery is doing with .ready() (docs). All your jQuery code that affects DOM element should be inside that event handler.

In fact, the jQuery tutorial explicitly states:

As almost everything we do when using jQuery reads or manipulates the document object model (DOM), we need to make sure that we start adding events etc. as soon as the DOM is ready.

To do this, we register a ready event for the document.

$(document).ready(function() {
   // do stuff when DOM is ready
});

Alternatively you can also use the shorthand syntax:

$(function() {
    // do stuff when DOM is ready
});

Both are equivalent.

views – Twig sets body class even when region is not rendered in DOM

In the html.html.twig file of my theme if added the following code to body_classes to add a body class if a region is not empty:

page.hero ? 'w-hero-bg',

So it looks like this:

{%
  set body_classes = (
    logged_in ? 'user-logged-in',
    not root_path ? 'path-frontpage' : 'path-' ~ root_path|clean_class,
    node_type ? 'page-node-type-' ~ node_type|clean_class,
    db_offline ? 'db-offline',
    page.hero ? 'w-hero-bg',
  )
%}

I’ve created a block with views containing an ‘Hero image’. I put a filter in place which checks if there is a media id available in the ‘Hero image’ field. If there is none available, the block is empty and is not displayed.

I’ve placed this block in the Hero region (page.hero). But even when the block is not displayed, and the region is not rendered in the DOM the class ‘w-hero-bg’ is still being attached to the body.

Is this a bug or am I missing something?

https://www.drupal.org/project/drupal/issues/953034 might be relevant, but I can’t determine if this is related as my region is not rendered in the DOM.

Javascript ,comparar o background de uma div pelo DOM

Bom dia senhores e senhoritas,eu estou fazendo uma comparação para alterar a cor de uma div se ela tiver com um background yellow pra green mas não entendo porque não está dando certo

function exerciseTwo() {
    let tagDiv = document.getElementsByTagName('div');
    for(let i = 0; i < tagDiv.length; i += 1) {
            if(tagDiv(i).style.backgroundColor == 'yellow') {
                tagDiv(i).style.backgroundColor = 'green';
      }
    }
}
exerciseTwo();

javascript – dom manipulation of yeoman app using api data

my apologies if this is posted in the wrong place. im working on a Yo project and understand i can get my code reviewed here. for the life of me the js dom modifications arent showing up! please could somone take a quick look!? ive masked the data to hide personal apikey. im running gulp. apprecaited many thanks

      <!doctype html>
<html class="no-js" lang="">
  <head>
    <meta charset="utf-8">
    <meta name="description" content="">

    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Dashboard</title>
    <link rel="apple-touch-icon" href="apple-touch-icon.png">
    <!-- Place favicon.ico in the root directory -->
    <!-- build:css styles/vendor.css -->
    <link rel="stylesheet" href="http://codereview.stackexchange.com/node_modules/bootstrap/dist/css/bootstrap.min.css"     type="text/css" />
    <!-- endbuild -->
    <!-- build:css styles/main.css -->
    <link rel="stylesheet" href="styles/main.css">
    <!-- endbuild -->
    <!-- build:js scripts/modernizr.js -->
    <script src="https://codereview.stackexchange.com/scripts/modernizr.js"></script>
    <!-- endbuild -->
  </head>
  <body>



  <div class="jumbotron">
    <h1 class="display-3">'Allo, 'Allo!</h1>
    <p class="lead">
      <ul id="text"></ul>
    <p><a class="btn btn-lg btn-success" href="#">Splendid!</a></p>
  </div>
 
  <div class="footer">

<!-- build:js scripts/vendor.js -->
<script type="text/javascript" src="/node_modules/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<!-- endbuild -->
<!-- build:js scripts/main.js -->
<script src="scripts/main.js"></script>
<!-- endbuild -->
     "use strict";

console.log(''Allo 'Allo!');




function createNode(element) {
    return document.createElement(element);
}

function append(parent, el) {
  return parent.appendChild(el);
}


const ul = document.getElementById('text');
const url = "https://api.abc.com;

fetch(url)
.then((resp) => resp.json())
.then(function(data) {
  let info = data.results;
  return info.map(function(stuff) {
    let li = createNode('li');
    let span = createNode('span');
    span.innerHTML = `${stuff.name} ${stuff.price}`;
    append(li, span);
    append(ul, li);
  })
})
.catch(function(error) {
  console.log(error);
});

 // Uncomment to enable Bootstrap tooltips
// https://getbootstrap.com/docs/4.0/components/tooltips/#example-enable-tooltips-everywhere
// $(function () { $('(data-toggle="tooltip")').tooltip(); });
// Uncomment to enable Bootstrap popovers
// https://getbootstrap.com/docs/4.0/components/popovers/#example-enable-popovers-everywhere
// $(function () { $('(data-toggle="popover")').popover(); });
//# sourceMappingURL=main.js.map

console.log(''Allo 'Allo!');




function createNode(element) {
    return document.createElement(element);
}

function append(parent, el) {
  return parent.appendChild(el);
}


const ul = document.getElementById('text');
const url = "https://api.abc.com;

fetch(url)
.then((resp) => resp.json())
.then(function(data) {
  let info = data.results;
  return info.map(function(stuff) {
    let li = createNode('li');
    let span = createNode('span');
    span.innerHTML = `${stuff.name} ${stuff.price}`;
    append(li, span);
    append(ul, li);
  })
})
.catch(function(error) {
  console.log(error);
});

 // Uncomment to enable Bootstrap tooltips
// https://getbootstrap.com/docs/4.0/components/tooltips/#example-enable-tooltips-everywhere
// $(function () { $('(data-toggle="tooltip")').tooltip(); });
// Uncomment to enable Bootstrap popovers
// https://getbootstrap.com/docs/4.0/components/popovers/#example-enable-popovers-everywhere
// $(function () { $('(data-toggle="popover")').popover(); });
//# sourceMappingURL=main.js.map

html – Why would you ever use the shadow DOM if you can’t apply global styles?

How can you expect to create re-usable components with the shadow DOM and also be expected to give it a separate style? How can anyone be able to share components with each other if that person can’t apply a style on top? I would never use anyone else’s components if they aren’t using my css library..

Side suggestion, <slot> should be useable in light dom with custom components.

javascript – How to abstract DOM manipulation in a simple intuitive way

I am building a library to abstract DOM manipulation. Here’s what the usage looks like:

  vsAddToSection() {

    let _inChapter: c.InChapter;

    let v$sections = vmap((), _ =>
      vh('div.section.item', {}, {
        listeners: {
          click: (e, __) => {
            this.ctrl.inSection(_inChapter, _)
          }
        }
      }, (h('span', _.name))));

    let v$backToABook = this.vBackToABook(),
    v$backToBook = this.vBackToBook();

    let update = (inChapter: c.InChapter) => {
      _inChapter = inChapter;
      v$backToBook.update(inChapter);
      v$backToBook.update(inChapter);
      v$sections.update(inChapter.sections);
    };

    return (update, (
      v$backToABook,
      v$backToBook,
      h('div.sections', v$sections),
      vh('button', {}, {
        listeners: {
          click: (e, _) => {
            this.ctrl.toNew.pub(_inChapter);
          }
        }
      }, (h('i', '+'), h('span', 'New Section')))
    ));
  }


  vAddToBookPopup() {

    let v$content = vex((h('div', 'default')));

    let (vs$addToBookUpdate, vs$addToBook) = this.vsAddToBook(),
    (vs$addToChapterUpdate, vs$addToChapter) = this.vsAddToChapter(),
    (vs$addToSectionUpdate, vs$addToSection) = this.vsAddToSection();




    this.ctrl.addTo.sub(addTo => {
      if (c.isInABook(addTo)) {
        vs$addToBookUpdate(addTo);
        v$content.replace(vs$addToBook);
      } else if (c.isInBook(addTo)) {
        vs$addToChapterUpdate(addTo);
        v$content.replace(vs$addToChapter);
      } else if (c.isInChapter(addTo)) {
        vs$addToSectionUpdate(addTo);
        v$content.replace(vs$addToSection);
      } else if (c.isInSection(addTo)) {
        // console.log(addTo);
        // selected
      }
    });

    return h('div.popup.addtobook', (v$content));
  }

vh creates a dom node, adds event listeners to it, etc.
h is a wrapper around vh.
vmap takes an array of data, and a function to create children dom nodes. The abstraction has an update method to update the array of data so it can remove old children dom nodes, and create and add new children.
vex takes an array of vh abstraction and has a replace method that replaces the dom contents with the new array of vh‘s.

this.ctrl.addTo.sub listens to an event that updates the dom, like this:

vs$addToBookUpdate(addTo);
v$content.replace(vs$addToBook);

This updates the contents of a piece of dom, and replaces the parent dom with that new piece of dom.

I return two values from vsAddToSection function, one for to update the abstraction, one returns the abstraction itself to append.

Hope this makes sense, my goal is simplicity, something that works, and avoid using external dependencies. Is this a little cumbersome, should I stick to a regular virtual dom approach?