最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Bootstrap tabs with CodeMirror - Stack Overflow

programmeradmin4浏览0评论

I have a pretty complex page where I have a number of instances of CodeMirror in hidden tabs within tabs. To then make it even more complex I remember the last active tabs.

I've manage to get it half working () the problems are with the Second Editor tabs:

  1. Its loading the Second tabs before the main Code Mirror tabs has been clicked. When you do click the Code Mirror tab it doesn't load the editor correctly either, until you click twice.
  2. I want the second tabs to call the refresh() method if its already been initiated, like I do for the main editor.
  3. Bug where its duplicating the secondary editors

(function($) {
    var mainEditor;

    function initMainCodeEditor() {
        if (mainEditor instanceof CodeMirror) {
            mainEditor.refresh();
        } else {
            // Load main editor
            var el = document.getElementById("codifyme");
            mainEditor = CodeMirror.fromTextArea(el, {
                lineNumbers: true
            });
            mainEditor.setSize('100%', 50);
        }
    }

    function initSecondaryCodeEditor() {
        var $active = $('#code_mirror_editors > .active > a');
        var $sec_tab = $($active.data('target'));

        CodeMirror.fromTextArea($sec_tab.find('textarea')[0], {
            lineNumbers: true
        });
    }

    $(document).ready(function() {

        // Only load editors if tab has been clicked
        $('#maintabs > li > a[data-target="#codemirror"]').on('shown.bs.tab', function(e) {
            initMainCodeEditor();
        });

        $('#code_mirror_editors > li > a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            initSecondaryCodeEditor();
        });

        // Remember tabs
        var json, tabsState;
        $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            tabsState = localStorage.getItem("tabs-state");
            json = JSON.parse(tabsState || "{}");
            json[$(e.target).parents("ul.nav.nav-pills, ul.nav.nav-tabs").attr("id")] = $(e.target).data('target');

            localStorage.setItem("tabs-state", JSON.stringify(json));
        });

        tabsState = localStorage.getItem("tabs-state");

        json = JSON.parse(tabsState || "{}");
        $.each(json, function(containerId, target) {
            return $("#" + containerId + " a[data-target=" + target + "]").tab('show');
        });

        $("ul.nav.nav-pills, ul.nav.nav-tabs").each(function() {
            var $this = $(this);
            if (!json[$this.attr("id")]) {
                return $this.find("a[data-toggle=tab]:first, a[data-toggle=pill]:first").tab("show");
            }
        });

    }); // doc.ready
})(jQuery);

I have a pretty complex page where I have a number of instances of CodeMirror in hidden tabs within tabs. To then make it even more complex I remember the last active tabs.

I've manage to get it half working (http://codepen.io/anon/pen/LheaF) the problems are with the Second Editor tabs:

  1. Its loading the Second tabs before the main Code Mirror tabs has been clicked. When you do click the Code Mirror tab it doesn't load the editor correctly either, until you click twice.
  2. I want the second tabs to call the refresh() method if its already been initiated, like I do for the main editor.
  3. Bug where its duplicating the secondary editors

(function($) {
    var mainEditor;

    function initMainCodeEditor() {
        if (mainEditor instanceof CodeMirror) {
            mainEditor.refresh();
        } else {
            // Load main editor
            var el = document.getElementById("codifyme");
            mainEditor = CodeMirror.fromTextArea(el, {
                lineNumbers: true
            });
            mainEditor.setSize('100%', 50);
        }
    }

    function initSecondaryCodeEditor() {
        var $active = $('#code_mirror_editors > .active > a');
        var $sec_tab = $($active.data('target'));

        CodeMirror.fromTextArea($sec_tab.find('textarea')[0], {
            lineNumbers: true
        });
    }

    $(document).ready(function() {

        // Only load editors if tab has been clicked
        $('#maintabs > li > a[data-target="#codemirror"]').on('shown.bs.tab', function(e) {
            initMainCodeEditor();
        });

        $('#code_mirror_editors > li > a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            initSecondaryCodeEditor();
        });

        // Remember tabs
        var json, tabsState;
        $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
            tabsState = localStorage.getItem("tabs-state");
            json = JSON.parse(tabsState || "{}");
            json[$(e.target).parents("ul.nav.nav-pills, ul.nav.nav-tabs").attr("id")] = $(e.target).data('target');

            localStorage.setItem("tabs-state", JSON.stringify(json));
        });

        tabsState = localStorage.getItem("tabs-state");

        json = JSON.parse(tabsState || "{}");
        $.each(json, function(containerId, target) {
            return $("#" + containerId + " a[data-target=" + target + "]").tab('show');
        });

        $("ul.nav.nav-pills, ul.nav.nav-tabs").each(function() {
            var $this = $(this);
            if (!json[$this.attr("id")]) {
                return $this.find("a[data-toggle=tab]:first, a[data-toggle=pill]:first").tab("show");
            }
        });

    }); // doc.ready
})(jQuery);
Share Improve this question asked Oct 21, 2014 at 13:01 John MagnoliaJohn Magnolia 16.8k39 gold badges169 silver badges274 bronze badges 0
Add a comment  | 

5 Answers 5

Reset to default 8 +250

The problems:

  1. it might happen that you create the CodeMirror on an element which is not visible (one of it's parents has display: none). This breaks various calculations done by CodeMirror
  2. by getting the CodeMirror instance right from the CodeMirror container element enables us to call refresh everytime you want (by finding the .CodeMirror next to your textarea)
  3. fixed as a side effect of 2.

HTML

Here's a bootstrap tab frame, if you use jquery-UI, it's gonna be a little different.

<ul class="nav nav-tabs">
    <li class="active"><a href="#tab1" data-toggle="tab">tab1</a></li>
    <li><a href="#tab2" data-toggle="tab">tab2</a></li>
</ul>

<div class="tab-content">
    <div class="tab-pane fade in active" id="tab1"><textarea type="text" id="tab1Content" name="xxx"></textarea></div>    
    <div class="tab-pane fade" id="tab2"><textarea type="text" id="tab2Content" name="yyy"></textarea></div>
</div>

JS

var cm1, cm2;
$(document).ready(function () {
    //when the bootstrap tab is fully shown, call codemirror's refresh().
    //Also, if you use jquery-UI, it's gonna be different here. 
    $('.nav-tabs a').on('shown.bs.tab', function() {
        cm1.refresh();
        cm2.refresh();
    });  
    cm1 = CodeMirror.fromTextArea(document.getElementById("tab1Content"), {
        lineNumbers: true
    });
    cm2 = CodeMirror.fromTextArea(document.getElementById("tab2Content"), {
        lineNumbers: true
    });
});

Working Solution: http://codepen.io/anon/pen/hupwy

I addressed issues 2 and 3 as I could not replicated 1.

I used a hash table to store the second editor CodeMirror objects. Then I modified your existing mainEditor code to refresh the objects if they already exist.

var seccondEditor = new Object();
function initSecondaryCodeEditor(){  
  var $active = $('#code_mirror_editors > .active > a');      
  var $sec_tab = $($active.data('target'));
  var sec_edi = ($sec_tab.find('textarea')[0]);
  if(seccondEditor[sec_edi.id] instanceof CodeMirror){
            seccondEditor[sec_edi.id].refresh();
      } else {
    seccondEditor[sec_edi.id] = CodeMirror.fromTextArea(sec_edi, {lineNumbers: true});
  }
}

I'm not well versed in CodeMirror so this might no be the most elegant solution, but it looks like the code above prevents the duplicates you were seeing. Hope this helps.

if the problem occurs when codemirror render content within an hidden div, why not just diplay all the tab-panel div, then call codemirror, then hide unneeded tab ??

HTML:

   <div role="tabpanel" class="tab-pane active" id="tp1">
       <textarea class="code" data-lang="text/html">
          <!--- content #1 here -->
       </textarea>
   </div>
   <div role="tabpanel" class="tab-pane active hideAfterRendering" id="tp2">
       <textarea class="code" data-lang="text/html">
          <!--- content #2 here -->
       </textarea>
   </div>
   <div role="tabpanel" class="tab-pane active hideAfterRendering" id="tp3">
       <textarea class="code" data-lang="text/html">
          <!--- content #3 here -->
       </textarea>
   </div>

SCRIPT:

   <script>
        $( document ).ready(function() {
            //render codeMirror
            $('.code').each(function() { 
                CodeMirror.fromTextArea(this, {
                    mode: "properties",
                    lineNumbers: true,
                    indentUnit: 4,
                    readOnly: true,
                    matchBrackets: true
                });
             });
            //hide tab-panel after codeMirror rendering (by removing the extra 'active' class
            $('.hideAfterRendering').each( function () {
                $(this).removeClass('active')
            });
        });
   </script>

Works well for me !!

Why not just to set timeout?.. just exec that code when tab clicked:

update_mirror = function() {
  var codeMirrorContainer = $sec_tab.find(".CodeMirror")[0];
  if (codeMirrorContainer && codeMirrorContainer.CodeMirror) {
    codeMirrorContainer.CodeMirror.refresh();
  }
}
// Attention! magic!
setTimeout(update_mirror, 100);

That helped me a lot while I had a battle against that problem. Thanks to Sekaryo Shin, he saved my time.

发布评论

评论列表(0)

  1. 暂无评论