Skip to main content
add tag, update grammar
Source Link

A few days ago, I created this rich text editor, and posted it to reddit and got over 300 upvotes in less than I1 day. I am a beginner, so I don't believe that this project is muchvery complex, but with your help I can improve it, and maybe someone will use this.

few days ago I created this rich text editor, and posted it to reddit and got over 300 upvotes in less than I day. I am a beginner so I don't believe that this project is much, but with your help I can improve it, and maybe someone will use this.

A few days ago, I created this rich text editor, and posted it to reddit and got over 300 upvotes in less than 1 day. I am a beginner, so I don't believe that this project is very complex, but with your help I can improve it, and maybe someone will use this.

added 6 characters in body
Source Link
Stoik
  • 81
  • 6

Live demo: https://jedit.herokuapp.com/https://panagram.herokuapp.com/

GitHub: https://github.com/Stoick001/jEditPanagram.js

import controlsPremade from './config';

const execute = (el, command, val) => {
  if (el && !command.includes('List')) {
    el.classList.toggle('active');
  }
  document.execCommand(command, false, val ? val: null);
}

export const init = (settings) => {
  const controls = controlsPremade[settings.controls];
  const ctrElement = settings.ctrElement;
  const outElement = settings.outElement;

  outElement.contentEditable = true;
  outElement.classList.add('output-el');

  ['click', 'touch'].forEach((evn) => {
    ctrElement.addEventListener(evn, (e) => {
      outElement.focus();
    });
  });

  outElement.addEventListener('keydown', event => {
    if (event.key === 'Tab') {
      event.preventDefault();
    } else if ((event.key === 'Enter' && document.queryCommandValue('formatBlock') === 'blockquote') || outElement.innerHTML === "" || outElement.innerHTML === "<br>") {
      setTimeout(() => {
        execute(null, 'formatBlock', '<div>');
      }, 0);
    }
  });

  controls.forEach(control => {
    const button = document.createElement('button');
    button.innerHTML = control.icon;
    button.title = control.title;
    button.setAttribute('type', 'button');
    button.classList.add('ctrl-btn');

    if (control.short) {
      document.addEventListener('keydown', (event) => {
        if (event.ctrlKey && event.key.toLowerCase() === control.short) {
            button.classList.toggle('active');
        }
      })
    }

    ['click', 'touch'].forEach((evn) => {
      if (control.state) {
        button.addEventListener(evn, () => execute(button, control.comName));
        ['keyup', 'mouseup'].forEach(cnt => {
          outElement.addEventListener(cnt, (e) => {
            if (document.queryCommandState(control.comName) && !control.comName.includes('List')) {
              button.classList.add('active');
            } else if (button.classList.contains('active')) {
              button.classList.remove('active');
            }
        });
      });
      } else if (control.formatBlock) {
        button.addEventListener(evn, () => execute(null, control.formatBlock, control.comName));
      } else {
        button.addEventListener(evn, () => {
          const val = (control.extra)();
          if (val) {
            execute(null, control.comName, val);
          }
        });
      }
    });

    ctrElement.append(button);
  });
}
export default {
  defaultControls: [
    {
      icon: '<i>B<'<b>B</b>', //fixed matching in tag surrounding icon text.
      title: 'Bold',
      comName: 'bold',
      state: true,
      short: 'b',
    }, {
      icon: '<i>I</i>',
      title: 'Italic',
      comName: 'italic',
      state: true,
      short: 'i',
    }, {
      icon: '<u>U</u>',
      title: 'Underline',
      comName: 'underline',
      state: true,
      short: 'u',
    }, {
      icon: '<b>H<sub>1</sub></b>',
      title: 'Heading 1',
      comName: '<h1>',
      formatBlock: 'formatBlock'
    }, {
      icon: '<b>H<sub>2</sub></b>',
      title: 'Heading 2',
      comName: '<h2>',
      formatBlock: 'formatBlock'
    }, {
      icon: 'P',
      title: 'Paragraph',
      comName: '<p>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#8220; &#8221;',
      title: 'Quote',
      comName: '<blockquote>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#35;',
      title: 'Ordered List',
      comName: 'insertOrderedList',
      state: true,
    }, {
      icon: '&#8226;',
      title: 'Unordered List',
      comName: 'insertUnorderedList',
      state: true,
    }, {
      icon: '&#128279;',
      title: 'Link',
      comName: 'createLink',
      extra: () => window.prompt('Enter the link URL'),
    }, {
      icon: '&#128247;',
      title: 'Image',
      comName: 'insertImage',
      extra: () => window.prompt('Enter the link URL'),
    },
  ]
}

EDIT: All code is now up to date.

Live demo: https://jedit.herokuapp.com/

GitHub: https://github.com/Stoick001/jEdit.js

import controlsPremade from './config';

const execute = (el, command, val) => {
  if (el && !command.includes('List')) {
    el.classList.toggle('active');
  }
  document.execCommand(command, false, val ? val: null);
}

export const init = (settings) => {
  const controls = controlsPremade[settings.controls];
  const ctrElement = settings.ctrElement;
  const outElement = settings.outElement;

  outElement.contentEditable = true;
  outElement.classList.add('output-el');

  ['click', 'touch'].forEach((evn) => {
    ctrElement.addEventListener(evn, (e) => {
      outElement.focus();
    });
  });

  outElement.addEventListener('keydown', event => {
    if (event.key === 'Tab') {
      event.preventDefault();
    }else if (event.key === 'Enter' && document.queryCommandValue('formatBlock') === 'blockquote') {
      setTimeout(() => {
        execute(null, 'formatBlock', '<div>');
      }, 0);
    }
  });

  controls.forEach(control => {
    const button = document.createElement('button');
    button.innerHTML = control.icon;
    button.title = control.title;
    button.setAttribute('type', 'button');
    button.classList.add('ctrl-btn');

    if (control.short) {
      document.addEventListener('keydown', (event) => {
        if (event.ctrlKey && event.key.toLowerCase() === control.short) {
            button.classList.toggle('active');
        }
      })
    }

    ['click', 'touch'].forEach((evn) => {
      if (control.state) {
        button.addEventListener(evn, () => execute(button, control.comName));
        ['keyup', 'mouseup'].forEach(cnt => {
          outElement.addEventListener(cnt, (e) => {
            if (document.queryCommandState(control.comName) && !control.comName.includes('List')) {
              button.classList.add('active');
            } else if (button.classList.contains('active')) {
              button.classList.remove('active');
            }
        });
      });
      } else if (control.formatBlock) {
        button.addEventListener(evn, () => execute(null, control.formatBlock, control.comName));
      } else {
        button.addEventListener(evn, () => {
          const val = (control.extra)();
          if (val) {
            execute(null, control.comName, val);
          }
        });
      }
    });

    ctrElement.append(button);
  });
}
export default {
  defaultControls: [
    {
      icon: '<i>B</b>',
      title: 'Bold',
      comName: 'bold',
      state: true,
      short: 'b',
    }, {
      icon: '<i>I</i>',
      title: 'Italic',
      comName: 'italic',
      state: true,
      short: 'i',
    }, {
      icon: '<u>U</u>',
      title: 'Underline',
      comName: 'underline',
      state: true,
      short: 'u',
    }, {
      icon: '<b>H<sub>1</sub></b>',
      title: 'Heading 1',
      comName: '<h1>',
      formatBlock: 'formatBlock'
    }, {
      icon: '<b>H<sub>2</sub></b>',
      title: 'Heading 2',
      comName: '<h2>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#8220; &#8221;',
      title: 'Quote',
      comName: '<blockquote>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#35;',
      title: 'Ordered List',
      comName: 'insertOrderedList',
      state: true,
    }, {
      icon: '&#8226;',
      title: 'Unordered List',
      comName: 'insertUnorderedList',
      state: true,
    }, {
      icon: '&#128279;',
      title: 'Link',
      comName: 'createLink',
      extra: () => window.prompt('Enter the link URL'),
    }, {
      icon: '&#128247;',
      title: 'Image',
      comName: 'insertImage',
      extra: () => window.prompt('Enter the link URL'),
    },
  ]
}

Live demo: https://panagram.herokuapp.com/

GitHub: https://github.com/Stoick001/Panagram.js

import controlsPremade from './config';

const execute = (el, command, val) => {
  if (el && !command.includes('List')) {
    el.classList.toggle('active');
  }
  document.execCommand(command, false, val ? val: null);
}

export const init = (settings) => {
  const controls = controlsPremade[settings.controls];
  const ctrElement = settings.ctrElement;
  const outElement = settings.outElement;

  outElement.contentEditable = true;
  outElement.classList.add('output-el');

  ['click', 'touch'].forEach((evn) => {
    ctrElement.addEventListener(evn, (e) => {
      outElement.focus();
    });
  });

  outElement.addEventListener('keydown', event => {
    if (event.key === 'Tab') {
      event.preventDefault();
    } else if ((event.key === 'Enter' && document.queryCommandValue('formatBlock') === 'blockquote') || outElement.innerHTML === "" || outElement.innerHTML === "<br>") {
      setTimeout(() => {
        execute(null, 'formatBlock', '<div>');
      }, 0);
    }
  });

  controls.forEach(control => {
    const button = document.createElement('button');
    button.innerHTML = control.icon;
    button.title = control.title;
    button.setAttribute('type', 'button');
    button.classList.add('ctrl-btn');

    if (control.short) {
      document.addEventListener('keydown', (event) => {
        if (event.ctrlKey && event.key.toLowerCase() === control.short) {
            button.classList.toggle('active');
        }
      })
    }

    ['click', 'touch'].forEach((evn) => {
      if (control.state) {
        button.addEventListener(evn, () => execute(button, control.comName));
        ['keyup', 'mouseup'].forEach(cnt => {
          outElement.addEventListener(cnt, (e) => {
            if (document.queryCommandState(control.comName) && !control.comName.includes('List')) {
              button.classList.add('active');
            } else if (button.classList.contains('active')) {
              button.classList.remove('active');
            }
        });
      });
      } else if (control.formatBlock) {
        button.addEventListener(evn, () => execute(null, control.formatBlock, control.comName));
      } else {
        button.addEventListener(evn, () => {
          const val = (control.extra)();
          if (val) {
            execute(null, control.comName, val);
          }
        });
      }
    });

    ctrElement.append(button);
  });
}
export default {
  defaultControls: [
    {
      icon: '<b>B</b>', //fixed matching in tag surrounding icon text.
      title: 'Bold',
      comName: 'bold',
      state: true,
      short: 'b',
    }, {
      icon: '<i>I</i>',
      title: 'Italic',
      comName: 'italic',
      state: true,
      short: 'i',
    }, {
      icon: '<u>U</u>',
      title: 'Underline',
      comName: 'underline',
      state: true,
      short: 'u',
    }, {
      icon: '<b>H<sub>1</sub></b>',
      title: 'Heading 1',
      comName: '<h1>',
      formatBlock: 'formatBlock'
    }, {
      icon: '<b>H<sub>2</sub></b>',
      title: 'Heading 2',
      comName: '<h2>',
      formatBlock: 'formatBlock'
    }, {
      icon: 'P',
      title: 'Paragraph',
      comName: '<p>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#8220; &#8221;',
      title: 'Quote',
      comName: '<blockquote>',
      formatBlock: 'formatBlock'
    }, {
      icon: '&#35;',
      title: 'Ordered List',
      comName: 'insertOrderedList',
      state: true,
    }, {
      icon: '&#8226;',
      title: 'Unordered List',
      comName: 'insertUnorderedList',
      state: true,
    }, {
      icon: '&#128279;',
      title: 'Link',
      comName: 'createLink',
      extra: () => window.prompt('Enter the link URL'),
    }, {
      icon: '&#128247;',
      title: 'Image',
      comName: 'insertImage',
      extra: () => window.prompt('Enter the link URL'),
    },
  ]
}

EDIT: All code is now up to date.

added 87 characters in body; edited tags
Source Link
200_success
  • 144.2k
  • 22
  • 188
  • 473
added 404 characters in body
Source Link
Stoik
  • 81
  • 6
Loading
Tweeted twitter.com/StackCodeReview/status/982230793379393536
Source Link
Stoik
  • 81
  • 6
Loading