I trying build a small useful js framework jsml but stuck

The problem is that the for Element when it re renders and new dom element is created that means if an element in the for element it unable to do transition when the state changes



import { mergeStringsWithSeparator,stopUnwanted ,stringSplitter} from './JSML/element.js';

import {removeJSMLWrapper,inheritParentStyles} from './JSML/utils.js';



const JSML = (call) => {

  let listeners = new Set();

  let templates = new Map();

  let keysMaps=new Map()

  let componentElement = ['IF', 'ELSEIF', 'CLONEABLE', 'CLONE', 'FOR', 'AWAIT'];



  const createDeepProxy = (target, callback) => {

    return new Proxy(target, {

      get(target, property) {

        const value = target[property];

        if (typeof value === 'object' && value !== null) {

          return createDeepProxy(value, callback);

        }

        return value;

      },

      set(target, property, value) {

        target[property] = value;

        callback();

        return true;

      },

      deleteProperty(target, property) {

        delete target[property];

        callback();

        return true;

      }

    });

  };



  const state = createDeepProxy(call(), () => {

    listeners.forEach(listener => listener());

  });



  const parsePlaceholders = (template, context) => {

  return template.replace(/{{(.*?)}}/g, (_, expression) => {

    try {

      return new Function(...Object.keys(context), `return ${expression}`)(...Object.values(context));

    } catch {

      return ''; // Return empty string for invalid placeholders

    }

  });

};



  const isValidPlaceholder = (value) => value !== undefined && value !== null && value !== '';



  const directives = {

    bind : (el, expression,nu ,context = {}) => {

  const evaluate = () => {

    const processedExp = parsePlaceholders(expression, context);



    // Check if the processed expression resolves in the current context

    try {

      const value = new Function("state", `return state.${processedExp}`)(state);

      el.textContent = value; // Update the DOM with the current value

    } catch (error) {

      console.warn(`Failed to evaluate bind expression: ${expression}`, error);

    }

  };

//console.log(context,nu)

  // Add a watcher to evaluate changes dynamically

  const addDynamicBinding = () => {

    const processedExp = parsePlaceholders(expression, context);



    let value = new Function("state", `return state.${processedExp}`)(state);



    Object.defineProperty(state, processedExp, {

      set(newValue) {

        value = newValue;

        el.textContent = newValue; // Reflect changes in DOM

      },

      get() {

        return value;

      },

    });

  };



  listeners.add(evaluate);

  evaluate();

  addDynamicBinding();

}, 

    click: (el, expression, context = {}) => {

      el.addEventListener('click', () => {

        let processedExp = expression;

        Object.entries(context).forEach(([key, value]) => {

          const replacementValue = typeof value === 'number' ? value : `'${value}'`;

          processedExp = processedExp.replace(new RegExp(`{{${key}}}`, 'g'), replacementValue);

        });

        new Function("state", `state.${processedExp}`)(state);

      });

    },

    xclass: (el, exp, context) => {

  const evaluate = () => {

    // Parse the expression into an object

    //console.log(exp)

    const classMap = new Function("state", ` { return ${exp}; }`)(state);

      //console.log(classMap)

     // return

    if (typeof classMap !== "object" || classMap === null) {

      console.warn(`Invalid xclass value: Expected an object, got ${typeof classMap}`);

      return;

    }



    // Loop through the classMap object

    for (const [className, condition] of Object.entries(classMap)) {

      if (condition) {

        let newClass=stringSplitter.byMultipleDelimiters(className.toString(),[','])

        newClass.forEach( nclass=> {

          el.classList.add(nclass)

        })

        //console.log(e)

      } else {

       let newClass = stringSplitter.byMultipleDelimiters(className.toString(), [','])

newClass.forEach(nclass => {

  el.classList.remove(nclass)

})

      }

    }

  };



  // Re-evaluate whenever the state changes

  listeners.add(evaluate);

  evaluate();

},

    xstyle: (el, expression, context = {}) => {

  const evaluate = () => {

    // Replace placeholders in the expression

   

    let processedExp = parsePlaceholders(expression, context);



    try {

      // Evaluate the processed expression

      const styles = new Function("state", `return ${processedExp}`)(state);

       

      // Apply the evaluated styles to the element

      Object.entries(styles).forEach(([key, value]) => {

        el.style[key] = value;

      });

    } catch (error) {

      console.error(`Error in xstyle directive: ${error.message}`);

    }

  };



  evaluate();

  listeners.add(evaluate);

}, 

    model: (el, expression, context = {}) => {

  const updateState = (value) => {

    //console.log(value)

    const processedExp = parsePlaceholders(expression, context);

    //

    if (Object.keys(context).length >0) {

      const split=stringSplitter.byMultipleDelimiters(processedExp,['.']);

      new Function("state", `return state.${context.arrayName}[${context.outerIndex}].${split[split.length -1]}=${value}`)(state)

      //console.log(processedExp)

    } else {

      new Function("state", `state.${processedExp} = ${value}`)(state);

    }

    

  };



  if (el.type === 'radio') {

    el.addEventListener('click', () => {

      const processedExp = parsePlaceholders(expression, context);

      const split=stringSplitter.byMultipleDelimiters(processedExp,['.']);

   const splitLength=split.length

     // console.log(processedExp,context)

      let currentValue 

      if (Object.keys(context).length > 0) {

        

      currentValue= new Function("state", `return state.${context.arrayName}[${context.outerIndex}].${split[splitLength -1]}`)(state)

      } else {

        currentValue=new Function("state", `return state.${processedExp}`)(state);

      }

       

      //console.log(currentValue)

      updateState(!currentValue); // Toggle the value

    });

  } else {

    el.addEventListener('input', (e) => {

      const value = el.type === 'number' ? parseFloat(e.target.value) : e.target.value;

      updateState(`'${value}'`);

    });

  }



  const evaluate = () => {

    const split=stringSplitter.byMultipleDelimiters(expression,['.']);

   const splitLength=split.length

    const processedExp = parsePlaceholders(expression, context);

   const merged = mergeStringsWithSeparator('.',`${context.arrayName}[${context.index}]`)

   const select=stopUnwanted(merged,/undefined[undefined]/i)

   if (select && Object.keys(context).length > 0) {

     return 

   }

   let value;

   if (Object.keys(context).length > 0) {

     value=new Function("state", `return state.${context.arrayName}[${context.outerIndex}].${split[splitLength -1]}`)(state)

   } else {

     

     if (split.length > 1) {

       return

     }

     

     value = new Function("state", `return state.${processedExp}`)(state);

   }

    

    

    

    if (el.type === 'radio') {

      el.checked = !!value; // Update the "checked" state

      

      //el.style.backgroundColor = value ? 'blue' : 'red'; // Change style based on state

    } else {

      el.value = value !== undefined ? value : '';

    }

  };



  evaluate();

  listeners.add(evaluate);

}

  };



  const ElementCom = {

    if: (el, context) => {

  const expression = el.getAttribute('x');



  const evaluate = () => {

    const processedExp = parsePlaceholders(expression, context);



    try {

      const condition = context ? new Function(...Object.keys(context), `return ${processedExp}`)(...Object.values(context)) : new Function('state', `return ${processedExp}`)(state);



      Array.from(el.children).forEach(child => {

        child.style.display = condition ? '' : 'none';

      });



      // Handle ELSE sibling

      const sibling = el.nextElementSibling;

      if (sibling && sibling.tagName === 'ELSE') {

        Array.from(sibling.children).forEach(child => {

          child.style.display = condition ? 'none' : '';

        });

      }

    } catch (error) {

      console.error(`Error in if directive: ${error.message}`);

    }

    

  };



  evaluate();

  listeners.add(evaluate);

  

},

    for: (el, context) => {

      

  if (!templates.has(el)) {

    templates.set(el, el.innerHTML);

  }

  if (!keysMaps.has(el.getAttribute('key'))) {

   if(el.getAttribute('key')) {

     keysMaps.set(el.getAttribute('key'),el)

   }

  } else {

    console.log(keysMaps.get(el.getAttribute('key')))

    console.log('esits')

    forLoopUpdater(el,keysMaps)

  }

  

  

//console.log(el)

  const template = templates.get(el);

  const [itemNames, , arrayName] = el.getAttribute('x').split(' ');

  let splitloopIndex=stringSplitter.byMultipleDelimiters(itemNames,[','])

const itemName=splitloopIndex[0]

  const hasInvalidPattern = (str) => {

    // Check for the exact pattern '.[].'

    

    const invalidPattern = /[]./;

    if (invalidPattern.test(str)) {

      

      return true;

    }

    return false;

  };



  const evaluate = () => {

    const processedExp = parsePlaceholders(arrayName, context);

    if (hasInvalidPattern(processedExp)) {

      return;

    }

    

    const array = new Function("state", `return state.${processedExp}`)(state);



    if (!Array.isArray(array)) return;



    el.innerHTML = '';



    array.forEach((item, outerIndex) => {

      const wrapper = document.createElement('div');

      wrapper.innerHTML = template;



      const processNode = (node) => {

        if (node.nodeType === 3) { // Text node

          const text = node.textContent;

          const newText = text.replace(

            new RegExp(`{{${itemName}}}`, 'g'),

            item

          ).replace(

            new RegExp(`{{${itemName}\.(.+?)}}`, 'g'),

            (_, prop) => item[prop]

          ).replace(

            new RegExp(`{{index}}`, 'g'),

            outerIndex // Outer index for the current loop

          ).replace(

            new RegExp(`{{${splitloopIndex[1]}}}`, 'g'),

            outerIndex // Outer index for the current loop

          ).replace(

            new RegExp(`{{outerIndex}}`, 'g'),

            outerIndex // Alias for outer index

          );

          node.textContent = newText;

        } else if (node.attributes) {

          Array.from(node.attributes).forEach(attr => {

            const newValue = attr.value

              .replace(new RegExp(`{{${itemName}}}`, 'g'), item)

              .replace(new RegExp(`{{${itemName}\.(.+?)}}`, 'g'),

                (_, prop) => item[prop])

              .replace(new RegExp(`{{index}}`, 'g'), outerIndex).replace(

            new RegExp(`{{${splitloopIndex[1]}}}`, 'g'),

            outerIndex // Outer index for the current loop

          ).replace(new RegExp(`{{outerIndex}}`, 'g'), outerIndex);

            attr.value = newValue;

          });

        }



        Array.from(node.childNodes).forEach(processNode);

      };



      Array.from(wrapper.childNodes).forEach(processNode);



      const children = Array.from(wrapper.children);



      children.forEach(child => {

        const childContext = {

          ...context,

          [itemName]: item,

          outerIndex, 

          arrayName// Alias for outer index

        };



        if (child.tagName === 'FOR') {

          const innerExp = child.getAttribute('x');

          const [innerItemName, , innerArrayName] = innerExp.split(' ');



          childContext[`${itemName}_index`] = outerIndex; // Preserve outer loop index

          applyElement(child, 'for', childContext);



        } else if (child.tagName === 'IF') {

          const exp = child.getAttribute('x');

          applyElement(child, 'if', childContext, exp);

        } else {

          attr.forEach(directive => {

            const exp = child.getAttribute(directive);

            

            if (exp) {

              

              apply(child, directive, exp, childContext);

            }

          });

        }

        const nextAttriToAllChildren=Array.from(child.querySelectorAll('*'))

        nextAttriToAllChildren.forEach(nextedChild =>{

          if (nextedChild.tagName === 'IF') {

            return

          } else if (nextedChild.tagName === 'FOR') {

            

          }

       attr.forEach(directive => {

            const exp = nextedChild.getAttribute(directive);

            

            if (exp) {

              

              apply(nextedChild, directive, exp, childContext);

            }

          });

        })

      });



      while (wrapper.firstChild) {

        el.appendChild(wrapper.firstChild);

      }

    });

  };



  listeners.add(evaluate);

  evaluate();

},

  };

const forLoopUpdater=(el,mapskey) => {

    const node=mapskey.get(el.getAttribute('key'))

    const processNode = (node) => {

  if (node.nodeType === 3) { // Text node

    const text = node.textContent;

    const newText = text.replace(

      new RegExp(`{{${itemName}}}`, 'g'),

      item

    ).replace(

      new RegExp(`{{${itemName}\.(.+?)}}`, 'g'),

      (_, prop) => item[prop]

    ).replace(

      new RegExp(`{{index}}`, 'g'),

      outerIndex // Outer index for the current loop

    ).replace(

      new RegExp(`{{${splitloopIndex[1]}}}`, 'g'),

      outerIndex // Outer index for the current loop

    ).replace(

      new RegExp(`{{outerIndex}}`, 'g'),

      outerIndex // Alias for outer index

    );

    node.textContent = newText;

  } else if (node.attributes) {

    Array.from(node.attributes).forEach(attr => {

      const newValue = attr.value

        .replace(new RegExp(`{{${itemName}}}`, 'g'), item)

        .replace(new RegExp(`{{${itemName}\.(.+?)}}`, 'g'),

          (_, prop) => item[prop])

        .replace(new RegExp(`{{index}}`, 'g'), outerIndex).replace(

          new RegExp(`{{${splitloopIndex[1]}}}`, 'g'),

          outerIndex // Outer index for the current loop

        ).replace(new RegExp(`{{outerIndex}}`, 'g'), outerIndex);

      attr.value = newValue;

    });

  }



  Array.from(node.childNodes).forEach(processNode);

};

}

  const applyElement = (el, Elements, context, expression) => {

    if (ElementCom[Elements]) {

      ElementCom[Elements](el, context, expression);

    }

  };



  const attr = Object.keys(directives);



  const apply = (el, directive, expression, context = {}) => {

    if (directives[directive]) {

      directives[directive](el, expression, context);

    }

  };



  const render = () => {

    const jhtmlElements = document.querySelectorAll('*');

    jhtmlElements.forEach(el => {

      const elements = Array.from(el.children);

      elements.forEach(element => {

        if (componentElement.includes(element.tagName)) {

          componentElement.forEach(com => {

            const AllComElement = document.querySelectorAll(com.toLowerCase());

            AllComElement.forEach(el => {

              applyElement(el, com.toLowerCase());

            });

          });

          return;

        }

        attr.forEach(directive => {

          const expression = element.getAttribute(directive);

          if (expression) {

            apply(element, directive, expression);

          }

        });

      });

    });

    componentElement.forEach(jml => {

      const jsml=document.querySelectorAll(jml)

      jsml.forEach(el => {

        inheritParentStyles(el)

      })

    })

  };



  render();



  return {

    state,

    render

  };

};



export default JSML;



This my library called JSML

<!DOCTYPE html>

<html>



<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>JSML</title>

  <script src="https://cdn.tailwindcss.com"></script>



</head>



<body>

  <div

      class="h-[35vh] transition-all

      transition duration-300 ease-in-out overflow-hidden bg-red-600 flex items-center justify-center"

      xstyle="{ 

        width: state.itemIndex === 0 ? '100vw' : '0px', 

        transform: state.itemIndex === 0 ? 'scale(1)' : 'scale(0)', 

        backgroundColor: state.itemIndex === 0 ? 'blue' : 'red' 

      }"

    >

  </div>

<div class="flex w-screen h-[35vh]">

  

    <div class=" w-screen h-[35vh]">

  <for x="item,i in items" parent="class" key="forloop">

    <div

      class="h-[35vh] transition-all

      transition duration-300 ease-in-out overflow-hidden bg-red-600 flex items-center justify-center"

      xstyle="{ 

        width: state.itemIndex === {{i}} ? '100vw' : '0px', 

        transform: state.itemIndex === {{i}} ? 'scale(1)' : 'scale(0)', 

        backgroundColor: state.itemIndex === {{i}} ? 'blue' : 'red' 

      }"

    >

      <div>

        <span>{{item}}</span>

        <div>

          <span bind="itemIndex"></span>

        </div>

        <div>

          <if x="state.isTrue">

            <span>istrue</span>

          </if>

        </div>

      </div>

  </for>

</div>

</div>

<script src="./text.js" type="module"></script>

</body>



</html>

That is the html code

import JSML from './index.js';



const app=JSML(()=> ({

  items:['1','2','3'],

  itemIndex:0,

  isTrue:false,

  data:true

  

}))

const carousel= () => {

return  setInterval(() => {

  

     app.state.itemIndex++

     if (app.state.itemIndex >= 3) {

  app.state.itemIndex = 0



  clearInterval(carousel)

}



  },4000)

}

carousel()


This is the text.js code

From the above please help me make the library for loop not to re-render but in stead update the for element because when ever it re-render it set in new dom and by so transition does not work so help for that in my library

I am expecting the for loop to like a robust type like react or vue and