Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

Sorry, you do not have permission to ask a question, You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please type your username.

Please type your E-Mail.

Please choose an appropriate title for the post.

Please choose the appropriate section so your post can be easily searched.

Please choose suitable Keywords Ex: post, video.

Browse

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Logo

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Navigation

  • Home
  • About Us
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • About Us
  • Contact Us
Home/ Questions/Q 6885

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise Latest Questions

Author
  • 60k
Author
Asked: November 27, 20242024-11-27T10:40:08+00:00 2024-11-27T10:40:08+00:00

Reactivity Anywhere – without virtual DOM

  • 60k

How did this got into my mind??

Virtual DOM can be referenced as the thing which just “introduced” me to the title of this post. What if we keep aside all that diffing, state stuff and focus on one thing: reactivity between the JavaScript and the DOM. Well, most of us are using libraries just to achieve this reactivity in their apps. But most of them implement a virtual DOM which keeps track of all the tags, states, variables, objects and whatnot and then syncs them with the real DOM. As said, things might get a little crazy doing all this stuff. So I just decided, why not just implement a crude example of all this “virtual DOM” thing without virtual DOM. Is this even achievable?? The answer is (0.5 * yes)!! For the sake of this post, lets call it “Reactivity Anywhere”

Disclaimer

This post might have things that seem vague and senseless. Also don't take things too seriously here, take them as just a thought. Reader discretion is advised.

Let's Start!!

Prerequisites

  • A web browser
  • JavaScript

Defining the global variables(precisely, stores)

In order to keep track of what goes here and there, we need some global variables to preserve and mutate all the state.

const __refs__ = {}; const __reactives__ = []; const __reactiveEl__ = document.querySelectorAll("[reactive]"); const reactive = (obj) => {   /*Add logic*/ }; const __updateDOM__ = (ref) => {   /*Add logic*/ }; 
Enter fullscreen mode Exit fullscreen mode

This is just everything we need for the logic. The variables whose names start and end with double underscores are __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.
We will only store two things: references to the elements, and, of course, the reactive variables.

But this seems like virtual DOM!!!

But I'm sorry, this is not the virtual DOM you think:

  • We will not be diffing the whole element tree for every single change; only and only the affected element will be mutated (less carbon dioxide)

Determining reactive elements

In order to keep specificity and refrain from scanning the whole DOM, we will just handpick special elements that work with our module. So, any element that has the reactive attribute (<element reactive></element>) , will only be able to use the special reactive powers.

In order to access the reactive elements from the store, we will use the ES6 string interpolation syntax. So, to access the count, we will write

<h1 reactive>The time is ${count}</h1> 
Enter fullscreen mode Exit fullscreen mode

The __refs__

Here, we will store the values of the object passed in the reactive function.

The __reactives__

This is just an array containing live references of the DOM Elements.

The reactive()

This function is basically a store where you'd be storing all the reactive stuff.
The definition of the function is surprisingly simple:

const reactive = (obj) => {   //Loop through the string   Object.keys(obj).forEach((key) => {     // defineProperty, anyone??     // We want to maintain reactivity, so we are using custom     // getters and setters     Object.defineProperty(__refs__, key, {       get() {         return obj[key];       },       // This shows an interesting aspect of the logic.       // This will update the target element everytime       // something changes.       set(val) {         obj[key] = val;         __updateDOM__(key);       },     });     // Think of this as an initial render     __updateDOM__(key);   });   // This is an important step otherwise   // everything is useless   return __refs__; }; 
Enter fullscreen mode Exit fullscreen mode

The __updateDOM__()

This is the Rosetta for the reactive DOM Elements and the __refs__. This function is also relative simple in its definition:

// Ref can be any key from the __refs__ store const __updateDOM__ = (ref) => {   // This is to check whether we want to update a specific ref value   if (ref) {     __reactives__       // filter out the elements we need       .filter((reactive) => reactive.dataset.ref === ref)       .forEach((reactive) => {         let ref = reactive.dataset.ref;         // Interacting with the DOM         // Nullish coalescing, anybody?         reactive.textContent = __refs__[ref] ?? "";       });   }   // UPDATE ALL!!   else     __reactives__.forEach((reactive) => {       let ref = reactive.dataset.ref;       // Interacting with the DOM       // Nullish coalescing, anybody?       reactive.textContent = __refs__[ref] ?? "";     }); }; 
Enter fullscreen mode Exit fullscreen mode

Finding all the reactive variables and bootstrapping them

This can basically be wrapped as an IIFE (Immediately Invoked Function Expression) but I don't consider doing it for the sake of simplicity. So, here we go!!

// Get live elements const __reactiveEl__ = document.querySelectorAll("[reactive]"); __reactiveEl__.forEach((el) => {   // Match the `count` between <h1 reactive>${count}</h1>   const refs = el.innerHTML.match(/${([^}]+)}/g);   // If the refs exist   if (refs) {     refs.forEach((ref) => {       // extract it       const dataRef = ref.split("{")[1].split("}")[0].trim();       // insert a special span element with the element       // and store the key name in the `data-ref` attribute       el.innerHTML = el.innerHTML.replace(         ref,         `<span class="reactivity-anywhere" data-ref="${dataRef}"></span>`       );     });     // Push the span element in __reactives__     __reactives__.push(...el.querySelectorAll("span.reactivity-anywhere"));   } }); // Initialize all the magic!! __updateDOM__(); 
Enter fullscreen mode Exit fullscreen mode

Making <input> and <textarea> work with reactives

Of course, we need this if user input is needed for our code to run.

The supercharged textareas and input elements will bear the ref attribute

A lot of things, harsh things are going to be done in this section, so brace yourself and hold on tight.

const parseDefaultRefValue = (el) => {   let parsed = null;   try {     // If the passed key is a function, run it     // and store the value     // I'm sorry, but we need to use eval here     parsed = eval(`(${el.getAttribute("ref-default")})()`);   } catch (e) {     parsed = el.getAttribute("ref-default");   }   return parsed; }; const assignDefaultRefsToInputs = (el, ref) => {   __refs__[ref] = parseDefaultRefValue(el); }; // Select input and textarea elements containing the // 'ref' attribute, where the attr. value refers to any // key in the __refs__ store. // The `ref-default` contains the default value for the `ref` // eg. // <textarea ref="name"></textarea> document.querySelectorAll("input[ref], textarea[ref]").forEach((el) => {   // Keep a ref to the ref!! Because we don't want to   // lose it in event listeners   const ref = el.getAttribute("ref");   if (ref) {     // lazily update default values     window.addEventListener("load", () => assignDefaultRefsToInputs(el, ref));     el.addEventListener("input", () => {       // again, a dumb reference to the ref       const elRef = ref;       // preserve default value       const defaultVal = parseDefaultRefValue(el);       // Set whatever value is typed as the ref value       // else, the default value       __refs__[elRef] = el.value !== "" ? el.value : defaultVal;       if (__refs__[elRef] !== defaultVal) {         // Keep rest of the input/textarea elements in sync         Array.from(document.querySelectorAll("input[ref], textarea[ref]"))           // Find input/textareas with same refs           .filter((el) => el.getAttribute("ref") === elRef)           // Keep their value in sync           .forEach((el) => (el.value = __refs__[elRef]));       }     });   } }); 
Enter fullscreen mode Exit fullscreen mode

We're almost done!

Now the only thing that remains is to write some HTML to check whether everything works!
So, here we go!!
Some more things to note here:

  • You can use multiple stores!! However, if you redeclare a key in the latter store, it will take precedence, not the first one

Why something like this would be great to use (according to me)

  • It will allow HTML to do its work and JS to do its own. Its not like “All HTML!” or “All JS!” but a harmony between the two (not to mention CSS here) that will appreciate the job these languages have to do.
  • Minimal overhead. As I said earlier, no virtual DOM, only real DOM (credits to Svelte) with some objects in memory

Limitations

You're going to think over this :), because this is just a crude implementation of an idea. So, feel free to critically think over it.

Ending notes

If you seem to be intrested in creating some sort of framwework with this, you're ready to go (some frameworks, using this idea, might even exist)! I'd also be happy to help you! Thank you for bearing with me in such a long post!

htmljavascriptreactivitywebdev
  • 0 0 Answers
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

Sidebar

Ask A Question

Stats

  • Questions 4k
  • Answers 0
  • Best Answers 0
  • Users 2k
  • Popular
  • Answers
  • Author

    ES6 - A beginners guide - Template Literals

    • 0 Answers
  • Author

    Understanding Higher Order Functions in JavaScript.

    • 0 Answers
  • Author

    Build a custom video chat app with Daily and Vue.js

    • 0 Answers

Top Members

Samantha Carter

Samantha Carter

  • 0 Questions
  • 20 Points
Begginer
Ella Lewis

Ella Lewis

  • 0 Questions
  • 20 Points
Begginer
Isaac Anderson

Isaac Anderson

  • 0 Questions
  • 20 Points
Begginer

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

Querify Question Shop: Explore Expert Solutions and Unique Q&A Merchandise

Querify Question Shop: Explore, ask, and connect. Join our vibrant Q&A community today!

About Us

  • About Us
  • Contact Us
  • All Users

Legal Stuff

  • Terms of Use
  • Privacy Policy
  • Cookie Policy

Help

  • Knowledge Base
  • Support

Follow

© 2022 Querify Question. All Rights Reserved

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.