DEV Community

Aad Pouw
Aad Pouw

Posted on

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.

Top comments (0)