Я пытаюсь создать небольшой полезный js-фреймворк jsml, но застрял [закрыто]CSS

Разбираемся в CSS
Ответить
Anonymous
 Я пытаюсь создать небольшой полезный js-фреймворк jsml, но застрял [закрыто]

Сообщение Anonymous »

Я создаю структуру js, называемую языком разметки JavaScript (JSML), но есть несколько проблем, с которыми я сталкиваюсь, особенно для элемента, который предназначен для циклического перебора массивов, он работает, но всегда перерисовывается при обновлении состояния.
Мне просто нужен хороший способ улучшить «за»
Это моя библиотека под названием JSML:

Код: Выделить всё

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;
Это HTML-код:

Код: Выделить всё






JSML








{{item}}




istrue









Это код text.js:

Код: Выделить всё

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()
Из вышесказанного, пожалуйста, помогите мне сделать так, чтобы библиотека цикла for не выполняла повторную визуализацию, а вместо этого обновляла элемент for, потому что при повторной визуализации он устанавливается в новый dom и, таким образом, происходит переход. не работает, так что помогите в моей библиотеке
Я ожидаю, что циклу for подойдет надежный тип, такой как реагирование или vue и

Подробнее здесь: https://stackoverflow.com/questions/793 ... -but-stuck
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «CSS»