April 11, 2026

Why I have ditched the QuerySelector

Why? The 'document.querySelector()' has many downfalls. There are many articles on the...

By Aad Pouw (@aadswebdesign) • 5 min read
Why I have ditched the QuerySelector

Why?

  • The 'document.querySelector()' has many downfalls.
    • There are many articles on the web to read more about it!
  • As I work the Javascript OOP way, I don't need it.

What I do instead and what I want to share here is this:

Connecting to the DOM directly.
What I do is making use of already available object keys in the dom tree:

  • document.activeElement (in callbacks and aside of event.target)
  • firstElementChild
  • lastElementChild
  • children
    • length
  • nextElementSibling
  • parentElement
  • previousElementSibling

Some Wisdom first:

If a parent element has just one child element.
The values of 'firstElementChild' and 'lastElementChild' are equal.
This means that 'lastElementChild' can be used too!
☛ Bad practice, because if there is a new element to be added at a later stage then things are broken.
☛ Good practice is just to stick to 'firstElementChild' and leave 'lastElementChild' free for future use.

How do I do that?

It's about creating a central point to work from.
Next is to create the objects out of them.
For this I use a Map() based function, called 'createObjects('obj_name',{})'.

Working it out:

      First a reflection of my used html body structure:

<body>
  <div class='wrap container display-flex relative'>
    <section class='top relative'>
      <header class='relative'>
        <h3 class='relative'>example heading.</h3>
      </header>
    </section>
    <main class='relative'>
      <main-content class='workbench-ctn absolute'>
        <content-item class='wb-content relative display-flex'>
          Dynamic content
        </content-item>
      </main-content>
    </main>
      <div class='controls-ctn relative display-flex'>
        <aside class='sidebar-ctrl top relative display-flex'>
          Some other elements
        </aside><!--sidebar-ctrl top-->
        <aside class='open-close-block absolute'>
        <details id='toolbar_toggle' class='toggle toolbar relative' open>
          <summary class='tb caret-up-uc relative' title='close toolbar'></summary>
          </details><!--toggle toolbar -->\
          <details id='menubar_toggle' class='toggle menubar relative' open>\
            <summary class='mb caret-left-uc relative' title='close menubar'></summary>
          </details><!-- toggle menubar -->

        </aside><!--open-close-block -->
        <aside class='sidebar-ctrl left relative display-flex'>
           Other elements, dynamically added or used for callbacks
        </aside><!--sidebar-ctrl left-->
      </div><!-- controls-ctn -->
  </div><!--div.wrap.container-->
</body>

Then a file that I've created for this and is called 'get_dom_objects.js'.

There are two functions in this file:

  • getDomObjects() (for elements that have been created already)
  • getDomObjectsExtended(obj_args) (for elements that not have been created yet or elements that will be used at a later stage).
import * as FT from './functions.js';
Enter fullscreen mode Exit fullscreen mode
export async function getDomObjects(){
  const get_objects = await FT.createObjects('base_obj',{
    //1. Obtaining the document.body
    body_elem: document.body,   
      //other objects
  });  

  //2. Creating a const for further use.
  const {body_elem} = get_objects;

  //3. Obtaining the very first element after the body
  /**
   * It's easy, each body element has a firstElementChild
     and what that is?
   * As I'm using the given example here, 
     it is the _'div.container'_ element.
   */   
  get_objects.wrap_ctn = body.firstElementChild;

  //4. For this I create another const and also for 
       further use.
  const {wrap_ctn} = get_objects;

  //5.
  /**
   * For getting the elements after the 'div.container',
     there are different ways to do that.
   * A. by using (not recommended!)
        'first/last' - 'ElementChild' & 
        'next/previous' -  'ElementSibling'.
   * B. by using (recommended) 'children.length'. 
   */
  // This way    
  if(wrap_ctn.children.length > 0){
    const wrap_children = wrap_ctn.children;
    get_objects.top_ctn = wrap_children[0];
    get_objects.main_elem = wrap_children[1];
  } 

  //6. Creating consts from that.
  const {top_ctn,main_elem} = get_objects;

  (I skipp the 'top_ctn' as there is no need for)
  //7. Getting the elements inside the 'main_elem'
  get_objects.workbench_ctn = main_elem.firstElementChild;
  get_objects.controlls_ctn = main_elem.lastElementChild;
  return get_objects;   
});
Enter fullscreen mode Exit fullscreen mode

Now I have all the objects that I'm going to use in getDomObjectsExtended(obj_args) function.

export async function getDomObjectsExtended(obj_args){
  const {body_elem,wrap_ctn,top_ctn,main_elem,
         workbench_ctn,controlls_ctn} = obj_args;
  const get_objects = await FT.createObjects(('ext_obj',{
    //1. Including the previously created objects    
    body_elem,main_elem,workbench_ctn,
    controlls_ctn,top_ctn,
  });
  //2. Moving on with creating the objects!
  get_objects.wb_content = workbench_ctn.firstElementChild;
  if(controlls_ctn.children.length > 0){
    const controls = controlls_ctn.children;
    get_objects.ctn_top = controls[0];
    get_objects.open_close_block = controls[1]; 
    get_objects.ctn_left = controls[2];  
  }
  const {open_close_block} = get_objects;
  get_objects.toggles = {
    //toolbar_toggle
    tb_toggle: open_close_block.firstElementChild,
    //menubar_toggle 
    mb_toggle: open_close_block.lastElementChild,   
  };        
  return get_objects;   
};
Enter fullscreen mode Exit fullscreen mode

Using those created objects:

  In my modules folder I have a file called modules_collect.js, this contains one function modulesCollect() and this is the file passed to my index.js.\
  Within that file:

  • I Collect all the imports from the sub folders.
  • I'm passing the objects to the classes & functions within those sub folders. #### The steps:

  ☛ In 'index.js' this:

  const dom_elems = await getDomObjects();
  await modulesCollect(dom_elems);
Enter fullscreen mode Exit fullscreen mode

  ☛ In 'modulesCollect' this:

export const modulesCollect = async (obj_args)=>{
  const obj_args_ext = await getDomObjectsExtended(obj_args);
  //Passing obj_args_ext to the _classes_ and _functions_   
  //This could be as a whole or partially {destructed}  
};
Enter fullscreen mode Exit fullscreen mode

Freedom for all:

It's about of how I do things.
If you're open to what I've presented, go for it!
If you're happy with the way you work already, stick to it!

That's it.

Ready to build your next web project? Let's work together.