создание элемента со слушателем

function CE(element, attribute, inner) {
    if (typeof element === 'undefined') return false
    if (typeof inner === 'undefined') inner = ''
    const el = document.createElement(element)
    if (typeof attribute === 'object') {
        for (const key in attribute) {
            const attr = attribute[key]
            if (Object.prototype.toString.call(attr).includes('Object')) {
                const entries = Object.entries(attr)
                switch (key) {
                    case 'data':
                        entries.map(([k, v]) => (el.dataset[k] = v))
                        break
                    case 'style':
                        entries.map(([k, v]) => (el.style[k] = v))
                        break
                }
            } else {
                if (key === 'checked' && attr === false) continue
                el.setAttribute(key, attr)
            }
        }
    }
    if (!Array.isArray(inner)) inner = [inner]
    for (const key of inner) {
        if (key?.tagName) el.appendChild(key)
        else if (key?.startsWith('<')) el.innerHTML = key
        else el.appendChild(document.createTextNode(key))
    }
    return el
}

// так всё работает
const menu = CE('div', { class: 'menu' }, [
    CE('div', { }, 'open'),
    CE('div', { }, 'copy'),
    CE('div', { data: { key: 'close' } }, 'close'),
    CE('div', { data: { key: 'open' } }, 'open')
])
document.body.append(menu)

дело в том что мне бы хотелось после создания элемента добавить слушателя типа:

// а так к сожалению ничего не работает
const menu = CE('div', { class: 'menu' }, [
    CE('div', { }, 'open').addEventListener('click', e => console.log(e), false),
    CE('div', { }, 'copy').addEventListener('click', e => console.log(e), false),
    CE('div', { data: { key: 'close' } }, 'close').addEventListener('click', e => console.log(e), false),
    CE('div', { data: { key: 'open' } }, 'open').addEventListener('click', e => console.log(e), false)
])

но после добавления слушателя всё ломается...

P.S. использование jQuery не вариант, а вот метод как там, хотелось бы.

пример: $('<div>',{text: 'title'}).on('click', e => console.log(e))


Ответы (2 шт):

Автор решения: Александр Сычёв

а что вам мешает, при создании в атрибуте передавать onclick: 'lol1()', например так сделать?

function CE(element, attribute, inner) {
    if (typeof element === 'undefined') return false
    if (typeof inner === 'undefined') inner = ''
    const el = document.createElement(element)
    if (typeof attribute === 'object') {
        for (const key in attribute) {
            const attr = attribute[key]
            if (Object.prototype.toString.call(attr).includes('Object')) {
                const entries = Object.entries(attr)
                switch (key) {
                    case 'data':
                        entries.map(([k, v]) => (el.dataset[k] = v))
                        break
                    case 'style':
                        entries.map(([k, v]) => (el.style[k] = v))
                        break
                }
            } else {
                if (key === 'checked' && attr === false) continue
                el.setAttribute(key, attr)
            }
        }
    }
    if (!Array.isArray(inner)) inner = [inner]
    for (const key of inner) {
        if (key?.tagName) el.appendChild(key)
        else if (key?.startsWith('<')) el.innerHTML = key
        else el.appendChild(document.createTextNode(key))
    }
    return el
}

// так всё работает
const menu = CE('div', { class: 'menu' }, [
    CE('div', {onclick: 'lol1()' }, 'open'),
    CE('div', {onclick: 'lol2()' }, 'copy'),
    CE('div', { data: { key: 'close' }, onclick: 'lol3()' }, 'close'),
    CE('div', { data: { key: 'open' }, onclick: 'lol4()' }, 'open')
])


document.querySelector('.out').append(menu)


function lol1(){
  console.log('work1')
}

function lol2(){
  console.log('work2')
}

function lol3(){
  console.log('work3')
}

function lol4(){
  console.log('work4')
}
<div class="out"></div>

→ Ссылка
Автор решения: Виталий Шебаниц

Как добавить еще один параметр вам уже написали. Опубликую ответ как бы я переделал вашу функцию. Я опустил ваши различные условия и пытался перенести смысл. Почему лучше...в основном потому что расширять такую структуру легче.

class Builder{  
  constructor(id){
    this.container = document.getElementById(id);
  }
  
  addItem(tag){
    return new MenuItem(tag, null, this)
  }
  
  render(element){
    this.container.append(element);
  }
}

class MenuItem {
  constructor(tag, parent = null, builder = null){
    this.tag = tag;
    this.parent = parent;
    this.builder = builder;
    this.element = document.createElement(tag);
  }
  
  addAttribute(attr,value){
    this.element.setAttribute(attr, value);
    return this;
  }
  
  addInnerItem(tag){
    return new MenuItem(tag, this)
  }
  
  addText(text){
    this.element.textContent = text;
    return this;
  }
  
  execute(){
    this.parent.element.append(this.element)
    return this.parent;
  }
  
  addEvent(type, fn){
    this.element.addEventListener(type, fn);
    return this;
  }
  
  build(){
   this.builder.render(this.element)
  }
  
  static isMenuItem(object){
    return object.itemName != undefined && object.attributes != undefined && object.innerElements != undefined;
  }
}

new Builder('container')
    .addItem('div')
  .addAttribute('class','menu')
  .addInnerItem('div')
    .addText('open')
    .addEvent('click', () => {
        alert('open')
    })
    .execute() 
  .addInnerItem('div')
    .addText('insert')
    .addEvent('click', () => {
        alert('insert')
    })
    .execute() 
  .build()
<div id="container">

</div>

→ Ссылка