Ich habe versucht, ein kleines nützliches JS-Framework (JSML) zu erstellen, bin aber hängengebliebenJavaScript

Javascript-Forum
Guest
 Ich habe versucht, ein kleines nützliches JS-Framework (JSML) zu erstellen, bin aber hängengeblieben

Post by Guest »

Das Problem besteht darin, dass das for-Element beim erneuten Rendern ein neues Dom-Element erstellt. Das heißt, wenn ein Element im for-Element keinen Übergang durchführen kann, wenn sich der Status ändert.
Dies heißt meine Bibliothek JSML:

Code: Select all

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;
Dies ist der HTML-Code:

Code: Select all






JSML



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'
}">





{{item}}




istrue









Dies ist der text.js-Code:

Code: Select all

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()
Bitte helfen Sie mir, die for-Schleife der Bibliothek so zu gestalten, dass sie nicht erneut gerendert wird, sondern stattdessen das for-Element aktualisiert, da es bei jedem erneuten Rendering in einen neuen Dom und damit in einen neuen Übergang gesetzt wird funktioniert nicht, also helfen Sie dafür in meiner Bibliothek
Ich erwarte, dass die for-Schleife einen robusten Typ wie „React“ oder „Vue“ mag und

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post