Title: Table options, DFW-Button
Last modified: February 11, 2026

---

# Table options, DFW-Button

 *  [Stefan Krapf](https://wordpress.org/support/users/synormedia/)
 * (@synormedia)
 * [3 months, 3 weeks ago](https://wordpress.org/support/topic/table-options-dfw-button/)
 * Hello Andrew,
   It’s been a long time since I wrote these two posts:[https://wordpress.org/support/topic/no-flyover-toolbar-on-tables-tab-key-doesnt-select-text/](https://wordpress.org/support/topic/no-flyover-toolbar-on-tables-tab-key-doesnt-select-text/)
   [https://wordpress.org/support/topic/two-separate-fullscreen-options/](https://wordpress.org/support/topic/two-separate-fullscreen-options/)
   But I haven’t actually found any solutions to the issues raised in all these 
   years – until now.With the help of Copilot, I was able to create a PHP script
   that actually solved most of these issues. The script has three parts. The first
   part does everything related to the tables:
    - Tab/Shift+Tab now selects the entire cell content after each press
    - Ctrl+A selects the entire content of the cell where the cursor is located
    - If you press Ctrl+A multiple times, the entire row is selected in the next
      step, followed by the entire table, and then the cell again (as in OneNote)
 * Then I mentioned the WordPress native full screen or DFW button, which cannot
   be hidden (because it does not really provide a correct full screen view). As
   you replied, it can be hidden via the menu at the top right. However, if you 
   have multiple users, this is a bit tedious because it cannot be controlled globally.
   Therefore, the second script simply hides this button. I actually wanted to completely
   disable the DFW function (with button and option), but I couldn’t do that with
   the script because my theme or WPBakery keeps reactivating this mode.
   Then the
   last part of the script activates the flyout menu for tables. Then I don’t have
   to install “Advanced TinyMCE Configuration” separately if I can solve it right
   here.So I would be particularly interested to hear what you think about the first
   script for tables. I can’t judge how good the quality of the PHP code is. But
   would it be possible to check this and build such a function directly into “Advanced
   Editor Tools”? Of course, it would be great if you could also hide the WP’s own
   full-screen button there, as only the full-screen button that can be displayed
   with “Advanced Editor Tools” works correctly! But I think that if you did, you
   wouldn’t want to simply hide the button using CSS, but rather have an option 
   to completely disable DFW mode. But I can’t implement that. Then there’s the 
   functionality of “Advanced TinyMCE Configuration.” Couldn’t you integrate those
   options into the “Advanced Editor Tools” plugin? Then you wouldn’t have to install
   another plugin, and the functionalities are very closely related thematically.
   So, those are my “nice to haves.” Let’s see what you think… 😉Many thanks, Stefan
 *     ```wp-block-code
       /** * Plugin Name:  SK TinyMCE Modifier * Description:  Sammel-Snippet für TinyMCE/Classic-Editor-Optimierungen * Version:      1.2.2 * Author:       Synor Media/Stefan Krapf mit Copilot 365 */if (!defined('ABSPATH')) exit;/* ============================================================================= * SECTION A — TinyMCE Tabellen-Booster * ----------------------------------------------------------------------------- * FUNKTION: * - Tab / Shift+Tab: *     springt zur nächsten/vorigen Tabellenzelle und markiert *     den *kompletten ZellINHALT*. Funktioniert auch in WPBakery/Impreza-Overlays. * * - Ctrl/Cmd + A (nur wenn Cursor in einer Tabellenzelle steht): *     1× = Zelle,  2× = ganze Zeile (TR),  3× = ganze Tabelle (TABLE), *     dann wieder Zelle (Zyklus wie in OneNote). * * - Beschränkung auf Editor-IDs: *     • content              (Haupteditor) *     • wpb_tinymce_content  (WPBakery/Impreza-Modal) * ========================================================================== *//** * Externes TinyMCE-Plugin registrieren (JavaScript wird via admin-ajax ausgeliefert). * Hinweis: Verwende in der URL normales '&' (kein HTML-escaped '&amp;'). * Erhöhe 'ver=' als Cachebuster, wenn du änderst. */add_filter('mce_external_plugins', function($external, $editor_id){    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $external;    $external['selectnextcell'] = admin_url('admin-ajax.php?action=sm_selectnextcell_js&ver=122');    return $external;}, 10, 2);/** * Pluginname 'selectnextcell' in die TinyMCE-Pluginliste hängen. * Hinweis: 'table' NICHT erzwingen (kann 404 erzeugen, wenn im Stack nicht vorhanden). */add_filter('tiny_mce_before_init', function($init, $editor_id){    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $init;    $plugins = isset($init['plugins']) ? $init['plugins'] : '';    if (is_array($plugins)) {        if (!in_array('selectnextcell', $plugins, true)) $plugins[] = 'selectnextcell';        $init['plugins'] = $plugins;    } else {        $set = array_filter(array_map('trim', preg_split('/[\s,]+/', (string)$plugins)));        if (!in_array('selectnextcell', $set, true)) $set[] = 'selectnextcell';        $init['plugins'] = implode(' ', $set);    }    return $init;}, 10, 2);/** * AJAX: Auslieferung des TinyMCE-Plugins (JavaScript) */add_action('wp_ajax_sm_selectnextcell_js', function(){    header('Content-Type: application/javascript; charset=utf-8');    ?>(function(){  if (!window.tinymce || !tinymce.PluginManager) return;  // --- Timing-Helfer: fn in n Frames ausführen ---  function afterFrames(fn, n){    var raf = window.requestAnimationFrame || function(f){ return setTimeout(f, 16); };    (function step(i){      if (i <= 0) { try { fn(); } catch(e){} return; }      raf(function(){ step(i-1); });    })(n||1);  }  // --- Auswahl-Helfer (TinyMCE-Selection, stabil für MCE4) ---  function setRangeOnNodeContents(editor, node){    var rng = editor.dom.createRng();    rng.setStart(node, 0);    rng.setEnd(node, node.childNodes.length);    editor.selection.setRng(rng);    editor.nodeChanged();  }  function selectCell(editor, cell){    if (!cell) return;    try {      editor.focus();      if (editor.getWin && editor.getWin().focus) editor.getWin().focus();      setRangeOnNodeContents(editor, cell);    } catch(ex){      try { editor.selection.select(cell, true); editor.nodeChanged(); } catch(_){}    }  }  function selectRow(editor, row){    if (!row) return;    try { setRangeOnNodeContents(editor, row); } catch(e){}  }  function selectTable(editor, table){    if (!table) return;    try { setRangeOnNodeContents(editor, table); } catch(e){}  }  // --- Nachbarzelle mit Wrap-Around innerhalb *derselben* Tabelle ---  function getNeighborCell(editor, curCell, dir){    if (!curCell) return null;    var table = editor.dom.getParent(curCell, 'table');    if (!table) return null;    // Alle Zellen (thead/tbody/tfoot inkl.)    var cells = editor.dom.select('td,th', table);    if (!cells || !cells.length) return null;    var idx = Array.prototype.indexOf.call(cells, curCell);    if (idx < 0) return null;    var nextIdx = (idx + (dir > 0 ? 1 : -1) + cells.length) % cells.length;    return cells[nextIdx];  }  // --- Kontext-Resolver: Bleib im Tabellenkontext, auch wenn Cursor auf TR/TABLE liegt ---  function getTableContext(editor){    // Nutze sowohl getNode() als auch den Range-Start    var node = editor.selection.getNode();    var rng  = editor.selection.getRng ? editor.selection.getRng() : null;    var sc   = rng ? rng.startContainer : null;    // Finde die Tabelle zuverlässig    var table = editor.dom.getParent(node, 'table') ||                (sc && editor.dom.getParent(sc, 'table')) || null;    if (!table) return {};    // Versuche eine Zelle zu finden    var cell = editor.dom.getParent(node, 'td,th') ||               (sc && editor.dom.getParent(sc, 'td,th')) || null;    // Versuche die Zeile zu finden (falls gebraucht)    var row  = editor.dom.getParent(node, 'tr')  ||               (cell && editor.dom.getParent(cell, 'tr')) ||               (sc && editor.dom.getParent(sc, 'tr')) || null;    // Falls keine Zelle ermittelbar: nimm letzte gemerkte Zelle innerhalb derselben Tabelle…    if (!cell) {      var st = editor.__sm_cycle && editor.__sm_cycle.lastCell;      if (st && editor.dom.getParent(st, 'table') === table) {        cell = st;      } else {        // …oder fallback auf die erste Zelle der Tabelle        var cells = editor.dom.select('td,th', table);        cell = (cells && cells[0]) ? cells[0] : null;        // Row ggf. aus dieser Zelle ableiten        if (!row && cell) row = editor.dom.getParent(cell, 'tr');      }    }    return { table: table, row: row, cell: cell };  }  // --- OneNote-Style Ctrl/Cmd + A: 1) Zelle 2) Zeile 3) Tabelle (dann wieder Zelle) ---  function cycleSelect(editor, cell){    var row   = editor.dom.getParent(cell, 'tr');    var table = editor.dom.getParent(cell, 'table');    if (!row || !table) { selectCell(editor, cell); return; }    editor.__sm_cycle = editor.__sm_cycle || { lastTable: null, lastCell: null, level: 0 };    // Kontextwechsel? Dann mit Level 1 beginnen    if (editor.__sm_cycle.lastTable !== table || editor.__sm_cycle.lastCell !== cell) {      editor.__sm_cycle.level = 1;    } else {      editor.__sm_cycle.level = (editor.__sm_cycle.level % 3) + 1; // 1..3    }    editor.__sm_cycle.lastTable = table;    editor.__sm_cycle.lastCell  = cell;    var doSelect = function(){      if (editor.__sm_cycle.level === 1)      selectCell(editor, cell);      else if (editor.__sm_cycle.level === 2) selectRow(editor, row);      else                                     selectTable(editor, table);    };    // Sofort + verzögert wiederholen (Caret/DOM-Resets überfahren)    doSelect();    afterFrames(doSelect, 1);    afterFrames(doSelect, 2);  }  function attach(editor){    if (!editor || editor.settings.__sm_attached) return;    editor.settings.__sm_attached = true;    // --- TAB / SHIFT+TAB: nächste/vorige Zelle + ZellINHALT markieren ---    function onTab(e){      if (e.keyCode !== 9) return; // Tab      var node = editor.selection.getNode();      var curCell = editor.dom.getParent(node, 'td,th');      if (!curCell) return; // außerhalb von Tabellen: Standardverhalten beibehalten      e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();      var dir    = e.shiftKey ? -1 : 1;      var target = getNeighborCell(editor, curCell, dir);      if (!target) return; // theoretisch nie, wegen Wrap-Around      // sofort + 1-2 Frames später auswählen (überfährt Caret-Resets)      selectCell(editor, target);      afterFrames(function(){ selectCell(editor, target); }, 1);      afterFrames(function(){ selectCell(editor, target); }, 2);    }    // --- CTRL/CMD + A im Tabellenkontext: Zelle → Zeile → Tabelle (Zyklus) ---    function onCtrlA(e){      var isCtrlA = (e.keyCode === 65) && (e.ctrlKey || e.metaKey);      if (!isCtrlA) return;      // Neu: Kontext ermitteln (auch wenn Cursor auf TR/TABLE sitzt)      var ctx = getTableContext(editor);      if (!ctx.table) return; // außerhalb von Tabellen: Standardverhalten      e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();      cycleSelect(editor, ctx.cell);    }    editor.on('keydown', onTab);    editor.on('keydown', onCtrlA);    // Capturing im IFRAME (hilft bei Modals/Overlays)    editor.on('init', function(){      try {        var doc = editor.getDoc();        var capTab = function(e){          if (e.keyCode !== 9) return;          var node = editor.selection.getNode();          var curCell = editor.dom.getParent(node, 'td,th');          if (!curCell) return;          e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();          var dir    = e.shiftKey ? -1 : 1;          var target = getNeighborCell(editor, curCell, dir);          if (!target) return;          selectCell(editor, target);          afterFrames(function(){ selectCell(editor, target); }, 1);          afterFrames(function(){ selectCell(editor, target); }, 2);        };        var capCtrlA = function(e){          var isCtrlA = (e.keyCode === 65) && (e.ctrlKey || e.metaKey);          if (!isCtrlA) return;          var ctx = getTableContext(editor);          if (!ctx.table) return;          e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();          cycleSelect(editor, ctx.cell);        };        doc.addEventListener('keydown', capTab,  true);        doc.addEventListener('keydown', capCtrlA, true);        editor.on('remove', function(){          try {            doc.removeEventListener('keydown', capTab,  true);            doc.removeEventListener('keydown', capCtrlA, true);          } catch(_){}        });      } catch(_) {}    });  }  // Registrierung als TinyMCE-Plugin  tinymce.PluginManager.add('selectnextcell', function(editor){    attach(editor);    return {};  });  // Für später erzeugte Editoren (z. B. Modal)  tinymce.on('AddEditor', function(e){ try { attach(e.editor); } catch(_){ } });})();    <?php    exit;});/* ============================================================================= * SECTION B — Classic Editor: DFW/Vollhöhe-Buttons ausblenden * ----------------------------------------------------------------------------- * Ziel: Nur UI verstecken. (Keine Änderung an der WP-Option "editor_expand"/DFW.) * - TinyMCE (visuell):    .mce-wp-dfw  (Container + Icon) * - Quicktags (Text/HTML): .qt-dfw und alle IDs qt_*_dfw (z. B. qt_content_dfw, qt_wpb_tinymce_content_dfw) * ========================================================================== */add_action('admin_head', function () {    if ( ! is_admin() ) return;    ?>    <style id="sm-hide-dfw-classic-editor">      /* (1) Quicktags – DFW-Buttons im Text-/HTML-Modus ausblenden         - deckt Haupteditor UND WPBakery/Overlays ab         - Beispiele: #qt_content_dfw, #qt_wpb_tinymce_content_dfw */      .quicktags-toolbar .qt-dfw,      [id^="qt_"][id$="_dfw"] {        display: none !important;      }      /* (2) TinyMCE – kompletter DFW-Button-Container + Inhalte ausblenden */      .mce-toolbar .mce-wp-dfw,      .mce-toolbar .mce-wp-dfw * {        display: none !important;      }      /* Lücke/Spacing vermeiden */      .mce-toolbar .mce-wp-dfw {        width: 0 !important;        margin: 0 !important;        padding: 0 !important;        border: 0 !important;      }      /* Optional enger scopen – nur Haupteditor:         #wp-content-editor-container .mce-toolbar .mce-wp-dfw { display:none !important; } */    </style>    <?php});/* ============================================================================= * SECTION C — TinyMCE: Table Toolbar Preset (ersetzt Advanced TinyMCE Config) * ----------------------------------------------------------------------------- * Setzt die table_toolbar wie im ATE-Plugin und stellt sicher, dass das * eingebaute 'table'-Plugin aktiv ist. * Gilt nur für die Editor-IDs: 'content', 'wpb_tinymce_content' * ========================================================================== */add_filter('tiny_mce_before_init', function($init, $editor_id){    // Nur unsere gewünschten Editoren    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $init;    // 1) table_toolbar wie definiert    $init['table_toolbar'] =        'tableprops tablerowprops tablecellprops | ' .        'tableinsertrowbefore tableinsertrowafter tabledeleterow | ' .        'tableinsertcolbefore tableinsertcolafter tabledeletecol | ' .        'tablemergecells tablesplitcells | tabledelete';    // 2) Sicherstellen, dass das 'table'-Plugin aktiv ist    $plugins = isset($init['plugins']) ? $init['plugins'] : '';    if (is_array($plugins)) {        if (!in_array('table', $plugins, true)) $plugins[] = 'table';        $init['plugins'] = $plugins;    } else {        $set = array_filter(array_map('trim', preg_split('/[\s,]+/', (string)$plugins)));        if (!in_array('table', $set, true)) $set[] = 'table';        $init['plugins'] = implode(' ', $set);    }    return $init;}, 10, 2);
       ```
   
 * The page I need help with: _[[log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fwordpress.org%2Fsupport%2Ftopic%2Ftable-options-dfw-button%2F%3Foutput_format%3Dmd&locale=en_US)
   to see the link]_

Viewing 2 replies - 1 through 2 (of 2 total)

 *  Thread Starter [Stefan Krapf](https://wordpress.org/support/users/synormedia/)
 * (@synormedia)
 * [3 months, 3 weeks ago](https://wordpress.org/support/topic/table-options-dfw-button/#post-18818574)
 * I have updated my script because I noticed that Advanced Editor Tool actually
   had a function that automatically created a new row in the last cell of a table
   when you pressed the tab key. This no longer worked with my script. I have now
   corrected this. Here is the updated version. I also tweaked the table flyout 
   menu a bit with CSS to make it more visible:
 *     ```wp-block-code
       /** * Plugin Name:  Synor TinyMCE Modifier * Description:  Sammel-Snippet für TinyMCE/Classic-Editor-Optimierungen * Version:      1.2.6 * Author:       Synor Media/Stefan Krapf mit Copilot 365 */if (!defined('ABSPATH')) exit;/* ============================================================================= * SECTION A — TinyMCE Tabellen-Booster * ----------------------------------------------------------------------------- * FUNKTION: * - Tab / Shift+Tab: *     springt zur nächsten/vorigen Tabellenzelle und markiert *     den *kompletten ZellINHALT*. Funktioniert auch in WPBakery/Impreza-Overlays. * * - Ctrl/Cmd + A (nur wenn Cursor in einer Tabellenzelle steht): *     1× = Zelle,  2× = ganze Zeile (TR),  3× = ganze Tabelle (TABLE), *     dann wieder Zelle (Zyklus wie in OneNote). * * - Beschränkung auf Editor-IDs: *     • content              (Haupteditor) *     • wpb_tinymce_content  (WPBakery/Impreza-Modal) * ========================================================================== *//** * Externes TinyMCE-Plugin registrieren (JavaScript wird via admin-ajax ausgeliefert). * Hinweis: Verwende in der URL normales '&' (kein HTML-escaped '&amp;'). * Erhöhe 'ver=' als Cachebuster, wenn du änderst. */add_filter('mce_external_plugins', function($external, $editor_id){    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $external;    $external['selectnextcell'] = admin_url('admin-ajax.php?action=sm_selectnextcell_js&ver=126');    return $external;}, 10, 2);/** * Pluginname 'selectnextcell' in die TinyMCE-Pluginliste hängen. * Hinweis: 'table' NICHT erzwingen (kann 404 erzeugen, wenn im Stack nicht vorhanden). */add_filter('tiny_mce_before_init', function($init, $editor_id){    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $init;    $plugins = isset($init['plugins']) ? $init['plugins'] : '';    if (is_array($plugins)) {        if (!in_array('selectnextcell', $plugins, true)) $plugins[] = 'selectnextcell';        $init['plugins'] = $plugins;    } else {        $set = array_filter(array_map('trim', preg_split('/[\s,]+/', (string)$plugins)));        if (!in_array('selectnextcell', $set, true)) $set[] = 'selectnextcell';        $init['plugins'] = implode(' ', $set);    }    return $init;}, 10, 2);/** * AJAX: Auslieferung des TinyMCE-Plugins (JavaScript) */add_action('wp_ajax_sm_selectnextcell_js', function(){    header('Content-Type: application/javascript; charset=utf-8');    ?>(function(){  if (!window.tinymce || !tinymce.PluginManager) return;  // --- Timing-Helfer: fn in n Frames ausführen ---  function afterFrames(fn, n){    var raf = window.requestAnimationFrame || function(f){ return setTimeout(f, 16); };    (function step(i){      if (i <= 0) { try { fn(); } catch(e){} return; }      raf(function(){ step(i-1); });    })(n||1);  }  // --- Auswahl-Helfer (TinyMCE-Selection, stabil für MCE4) ---  function setRangeOnNodeContents(editor, node){    var rng = editor.dom.createRng();    rng.setStart(node, 0);    rng.setEnd(node, node.childNodes.length);    editor.selection.setRng(rng);    editor.nodeChanged();  }  function selectCell(editor, cell){    if (!cell) return;    try {      editor.focus();      if (editor.getWin && editor.getWin().focus) editor.getWin().focus();      setRangeOnNodeContents(editor, cell);    } catch(ex){      try { editor.selection.select(cell, true); editor.nodeChanged(); } catch(_){}    }  }  function selectRow(editor, row){    if (!row) return;    try { setRangeOnNodeContents(editor, row); } catch(e){}  }  function selectTable(editor, table){    if (!table) return;    try { setRangeOnNodeContents(editor, table); } catch(e){}  }  // --- Nachbarzelle mit Wrap-Around innerhalb *derselben* Tabelle ---  function getNeighborCell(editor, curCell, dir){    if (!curCell) return null;    var table = editor.dom.getParent(curCell, 'table');    if (!table) return null;    // Alle Zellen (thead/tbody/tfoot inkl.)    var cells = editor.dom.select('td,th', table);    if (!cells || !cells.length) return null;    var idx = Array.prototype.indexOf.call(cells, curCell);    if (idx < 0) return null;    var nextIdx = (idx + (dir > 0 ? 1 : -1) + cells.length) % cells.length;    return cells[nextIdx];  }  // --- Kontext-Resolver: Bleib im Tabellenkontext, auch wenn Cursor auf TR/TABLE liegt ---  function getTableContext(editor){    // Nutze sowohl getNode() als auch den Range-Start    var node = editor.selection.getNode();    var rng  = editor.selection.getRng ? editor.selection.getRng() : null;    var sc   = rng ? rng.startContainer : null;    // Finde die Tabelle zuverlässig    var table = editor.dom.getParent(node, 'table') ||                (sc && editor.dom.getParent(sc, 'table')) || null;    if (!table) return {};    // Versuche eine Zelle zu finden    var cell = editor.dom.getParent(node, 'td,th') ||               (sc && editor.dom.getParent(sc, 'td,th')) || null;    // Versuche die Zeile zu finden (falls gebraucht)    var row  = editor.dom.getParent(node, 'tr')  ||               (cell && editor.dom.getParent(cell, 'tr')) ||               (sc && editor.dom.getParent(sc, 'tr')) || null;    // Falls keine Zelle ermittelbar: nimm letzte gemerkte Zelle innerhalb derselben Tabelle…    if (!cell) {      var st = editor.__sm_cycle && editor.__sm_cycle.lastCell;      if (st && editor.dom.getParent(st, 'table') === table) {        cell = st;      } else {        // …oder fallback auf die erste Zelle der Tabelle        var cells = editor.dom.select('td,th', table);        cell = (cells && cells[0]) ? cells[0] : null;        // Row ggf. aus dieser Zelle ableiten        if (!row && cell) row = editor.dom.getParent(cell, 'tr');      }    }    return { table: table, row: row, cell: cell };  }  // --- OneNote-Style Ctrl/Cmd + A: 1) Zelle 2) Zeile 3) Tabelle (dann wieder Zelle) ---  function cycleSelect(editor, cell){    var row   = editor.dom.getParent(cell, 'tr');    var table = editor.dom.getParent(cell, 'table');    if (!row || !table) { selectCell(editor, cell); return; }    editor.__sm_cycle = editor.__sm_cycle || { lastTable: null, lastCell: null, level: 0 };    // Kontextwechsel? Dann mit Level 1 beginnen    if (editor.__sm_cycle.lastTable !== table || editor.__sm_cycle.lastCell !== cell) {      editor.__sm_cycle.level = 1;    } else {      editor.__sm_cycle.level = (editor.__sm_cycle.level % 3) + 1; // 1..3    }    editor.__sm_cycle.lastTable = table;    editor.__sm_cycle.lastCell  = cell;    var doSelect = function(){      if (editor.__sm_cycle.level === 1)      selectCell(editor, cell);      else if (editor.__sm_cycle.level === 2) selectRow(editor, row);      else                                     selectTable(editor, table);    };    // Sofort + verzögert wiederholen (Caret/DOM-Resets überfahren)    doSelect();    afterFrames(doSelect, 1);    afterFrames(doSelect, 2);  }  function attach(editor){    if (!editor || editor.settings.__sm_attached) return;    editor.settings.__sm_attached = true;    // --- TAB / SHIFT+TAB: nächste/vorige Zelle + ZellINHALT markieren ---    function onTab(e){      if (e.keyCode !== 9) return; // Tab      var node = editor.selection.getNode();      var curCell = editor.dom.getParent(node, 'td,th');      if (!curCell) return; // außerhalb von Tabellen: Standardverhalten beibehalten      var table = editor.dom.getParent(curCell, 'table');      if (!table) return;      var cells = editor.dom.select('td,th', table);      if (!cells || !cells.length) return;      // LETZTE ZELLE + TAB (ohne Shift): Zeile selbst einfügen + in neue Zeile springen (Fokus sicher halten)      if (!e.shiftKey && curCell === cells[cells.length - 1]) {        e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();        // aktuelle TR bestimmen        var curRow = editor.dom.getParent(curCell, 'tr');        // Zeile *nach* der aktuellen einfügen (TinyMCE Table-Command)        try { editor.execCommand('mceTableInsertRowAfter'); } catch(_){}        // Nach Einfügen die nächste Zeile ermitteln und deren erste Zelle fokussieren        afterFrames(function(){          try {            var tableNow = editor.dom.getParent(curCell, 'table') || table;            var rows = editor.dom.select('tr', tableNow);            var rowIdx = Array.prototype.indexOf.call(rows, curRow);            var nextRow = (rowIdx >= 0) ? rows[rowIdx + 1] : null;            var firstCell = nextRow ? editor.dom.select('td,th', nextRow)[0] : null;            if (firstCell) {              // Deine gewohnte Auswahl-Logik beibehalten (ZellINHALT markieren)              selectCell(editor, firstCell);              afterFrames(function(){ selectCell(editor, firstCell); }, 1);              afterFrames(function(){ selectCell(editor, firstCell); }, 2);            }          } catch(_){}        }, 1);        return;      }      // --- Standardfall: Navigation innerhalb der Tabelle (inkl. Wrap-Around) ---      e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();      var dir    = e.shiftKey ? -1 : 1;      var target = getNeighborCell(editor, curCell, dir);      if (!target) return; // theoretisch nie, wegen Wrap-Around      // sofort + 1-2 Frames später auswählen (überfährt Caret-Resets)      selectCell(editor, target);      afterFrames(function(){ selectCell(editor, target); }, 1);      afterFrames(function(){ selectCell(editor, target); }, 2);    }    // --- CTRL/CMD + A im Tabellenkontext: Zelle → Zeile → Tabelle (Zyklus) ---    function onCtrlA(e){      var isCtrlA = (e.keyCode === 65) && (e.ctrlKey || e.metaKey);      if (!isCtrlA) return;      // Neu: Kontext ermitteln (auch wenn Cursor auf TR/TABLE sitzt)      var ctx = getTableContext(editor);      if (!ctx.table) return; // außerhalb von Tabellen: Standardverhalten      e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();      cycleSelect(editor, ctx.cell);    }    editor.on('keydown', onTab);    editor.on('keydown', onCtrlA);    // Capturing im IFRAME (hilft bei Modals/Overlays)    editor.on('init', function(){      try {        var doc = editor.getDoc();        var capTab = function(e){          if (e.keyCode !== 9) return;          var node = editor.selection.getNode();          var curCell = editor.dom.getParent(node, 'td,th');          if (!curCell) return;          var table = editor.dom.getParent(curCell, 'table');          if (!table) return;          var cells = editor.dom.select('td,th', table);          if (!cells || !cells.length) return;          // LETZTE ZELLE – Zeile selbst einfügen + in neue Zeile springen (Fokus sicher halten)          if (!e.shiftKey && curCell === cells[cells.length - 1]) {            e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();            var curRow = editor.dom.getParent(curCell, 'tr');            try { editor.execCommand('mceTableInsertRowAfter'); } catch(_){}            afterFrames(function(){              try {                var tableNow = editor.dom.getParent(curCell, 'table') || table;                var rows = editor.dom.select('tr', tableNow);                var rowIdx = Array.prototype.indexOf.call(rows, curRow);                var nextRow = (rowIdx >= 0) ? rows[rowIdx + 1] : null;                var firstCell = nextRow ? editor.dom.select('td,th', nextRow)[0] : null;                if (firstCell) {                  selectCell(editor, firstCell);                  afterFrames(function(){ selectCell(editor, firstCell); }, 1);                  afterFrames(function(){ selectCell(editor, firstCell); }, 2);                }              } catch(_){}            }, 1);            return;          }          e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();          var dir    = e.shiftKey ? -1 : 1;          var target = getNeighborCell(editor, curCell, dir);          if (!target) return;          selectCell(editor, target);          afterFrames(function(){ selectCell(editor, target); }, 1);          afterFrames(function(){ selectCell(editor, target); }, 2);        };        var capCtrlA = function(e){          var isCtrlA = (e.keyCode === 65) && (e.ctrlKey || e.metaKey);          if (!isCtrlA) return;          var ctx = getTableContext(editor);          if (!ctx.table) return;          e.preventDefault(); e.stopPropagation(); if (e.stopImmediatePropagation) e.stopImmediatePropagation();          cycleSelect(editor, ctx.cell);        };        doc.addEventListener('keydown', capTab,  true);        doc.addEventListener('keydown', capCtrlA, true);        editor.on('remove', function(){          try {            doc.removeEventListener('keydown', capTab,  true);            doc.removeEventListener('keydown', capCtrlA, true);          } catch(_){}        });      } catch(_) {}    });  }  // Registrierung als TinyMCE-Plugin  tinymce.PluginManager.add('selectnextcell', function(editor){    attach(editor);    return {};  });  // Für später erzeugte Editoren (z. B. Modal)  tinymce.on('AddEditor', function(e){ try { attach(e.editor); } catch(_){ } });})();    <?php    exit;});/* ============================================================================= * SECTION B — Classic Editor: DFW/Vollhöhe-Buttons ausblenden * ----------------------------------------------------------------------------- * Ziel: Nur UI verstecken. (Keine Änderung an der WP-Option "editor_expand"/DFW.) * - TinyMCE (visuell):    .mce-wp-dfw  (Container + Icon) * - Quicktags (Text/HTML): .qt-dfw und alle IDs qt_*_dfw (z. B. qt_content_dfw, qt_wpb_tinymce_content_dfw) * ========================================================================== */add_action('admin_head', function () {    if ( ! is_admin() ) return;    ?>    <style id="sm-hide-dfw-classic-editor">      /* (1) Quicktags – DFW-Buttons im Text-/HTML-Modus ausblenden         - deckt Haupteditor UND WPBakery/Overlays ab         - Beispiele: #qt_content_dfw, #qt_wpb_tinymce_content_dfw */      .quicktags-toolbar .qt-dfw,      [id^="qt_"][id$="_dfw"] {        display: none !important;      }      /* (2) TinyMCE – kompletter DFW-Button-Container + Inhalte ausblenden */      .mce-toolbar .mce-wp-dfw,      .mce-toolbar .mce-wp-dfw * {        display: none !important;      }      /* Lücke/Spacing vermeiden */      .mce-toolbar .mce-wp-dfw {        width: 0 !important;        margin: 0 !important;        padding: 0 !important;        border: 0 !important;      }      /* Optional enger scopen – nur Haupteditor:         #wp-content-editor-container .mce-toolbar .mce-wp-dfw { display:none !important; } */    </style>    <?php});/* ============================================================================= * SECTION C — TinyMCE: Table Toolbar Preset (ersetzt Advanced TinyMCE Config) * ----------------------------------------------------------------------------- * Setzt die table_toolbar wie im ATE-Plugin und stellt sicher, dass das * eingebaute 'table'-Plugin aktiv ist. * Gilt nur für die Editor-IDs: 'content', 'wpb_tinymce_content' * ========================================================================= */add_filter('tiny_mce_before_init', function($init, $editor_id){    // Nur unsere gewünschten Editoren    $allow = array('content', 'wpb_tinymce_content');    if (!in_array($editor_id, $allow, true)) return $init;    // 1) table_toolbar wie definiert    $init['table_toolbar'] =        'tableprops tablerowprops tablecellprops | ' .        'tableinsertrowbefore tableinsertrowafter tabledeleterow | ' .        'tableinsertcolbefore tableinsertcolafter tabledeletecol | ' .        'tablemergecells tablesplitcells | tabledelete';    // 2) Sicherstellen, dass das 'table'-Plugin aktiv ist    $plugins = isset($init['plugins']) ? $init['plugins'] : '';    if (is_array($plugins)) {        if (!in_array('table', $plugins, true)) $plugins[] = 'table';        $init['plugins'] = $plugins;    } else {        $set = array_filter(array_map('trim', preg_split('/[\s,]+/', (string)$plugins)));        if (!in_array('table', $set, true)) $set[] = 'table';        $init['plugins'] = implode(' ', $set);    }    return $init;}, 10, 2);/* ----------------------------------------------------------------------------- * TinyMCE Table-Flyout: Schatten (richtiger Container: .mce-floatpanel) * -------------------------------------------------------------------------- */add_action('admin_head', function () {    ?>    <style id="sm-tinymce-flyout-shadow">        /* Flyout-Container */        .mce-floatpanel.mce-tinymce-inline {            box-shadow:                0 1px 2px rgba(0,0,0,0.25),                0 4px 10px rgba(0,0,0,0.12),                0 12px 24px rgba(0,0,0,0.10);            border-radius: 6px;            /* dezente Abrundung */			border-color: #5c5c5c;			border-style: solid;            overflow: visible;             /* falls TinyMCE clipped */        }    </style>    <?php});
       ```
   
 *  Plugin Author [Andrew Ozz](https://wordpress.org/support/users/azaozz/)
 * (@azaozz)
 * [3 months, 3 weeks ago](https://wordpress.org/support/topic/table-options-dfw-button/#post-18823328)
 * Hi [@synormedia](https://wordpress.org/support/users/synormedia/), good job making
   a WP plugin!
 * > would it be possible to check this and build such a function directly into “
   > Advanced Editor Tools”
 * Frankly I think it would be better as it is now: a separate WP plugin. You can
   add it to the [WordPress.org plugin repository](https://wordpress.org/plugins/developers/).
 * > what you think about the first script for tables. I can’t judge how good the
   > quality of the PHP code is
 * TinyMCE (the old version that’s in WP) has a way to load its own plugins. Unfortunately
   the [documentation for TinyMCE 4.x](https://github.com/tinymce/tinymce-docs-4x/blob/main/general-configuration-guide/work-with-plugins.md)
   is not on a website any more, only the code is on Github. It will be harder to
   make the JS into a proper TinyMCE plugin. But if the plugin works satisfactory
   as it is at the moment, perhaps it would be good to release it so others can 
   use it too 🙂

Viewing 2 replies - 1 through 2 (of 2 total)

You must be [logged in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fwordpress.org%2Fsupport%2Ftopic%2Ftable-options-dfw-button%2F%3Foutput_format%3Dmd&locale=en_US)
to reply to this topic.

 * ![](https://ps.w.org/tinymce-advanced/assets/icon-256x256.png?rev=971511)
 * [Advanced Editor Tools](https://wordpress.org/plugins/tinymce-advanced/)
 * [Frequently Asked Questions](https://wordpress.org/plugins/tinymce-advanced/#faq)
 * [Support Threads](https://wordpress.org/support/plugin/tinymce-advanced/)
 * [Active Topics](https://wordpress.org/support/plugin/tinymce-advanced/active/)
 * [Unresolved Topics](https://wordpress.org/support/plugin/tinymce-advanced/unresolved/)
 * [Reviews](https://wordpress.org/support/plugin/tinymce-advanced/reviews/)

 * 2 replies
 * 2 participants
 * Last reply from: [Andrew Ozz](https://wordpress.org/support/users/azaozz/)
 * Last activity: [3 months, 3 weeks ago](https://wordpress.org/support/topic/table-options-dfw-button/#post-18823328)
 * Status: not resolved