0

I'm working in SharePoint 2013 and I'm building a dynamic multi-level menu system. Basically there are two components, a list where users add their menu items, and a page where using REST API and Dynamic CSS, the menu items are displayed.

Here is a representational sample of the REST API Code:


$(function () {    
    $.ajax({
        url: "/sites/regulatory3/_api/web/lists/GetByTitle('Navigation')/items?$orderby=LVL2Sequence",        
        type: "GET",
        headers: {
            "accept": "application/json;odata=verbose"
        },
    }).success(function (data) {

        var listItemInfo = '';

        listItemInfo += "<nav><ul id='nav'>"        

        //Level 1 LVL1Sequence 1
        $.each(data.d.results, function (key, value) {

           //alert("Sequence " + value.Sequence);  

           if(value.Level == 1){               

             if(value.Sequence == 1){
              //listItemInfo +=  value.Title;  
              listItemInfo += "<li><a href='#'>" + value.Title + "</a><ul>";                                 
             }             
           }                           
        }); 

        //TopItemID 1 Level 2 LVL2Sequence All
        $.each(data.d.results, function (key, value) {

        if(value.Level == 2){

             if(value.TopItemID == 1){
               listItemInfo += "<li><a href='" + value.Link + "'>" + value.Title + "</a></li>";
               }                                      
           }                       
        });          

        //Level 1 LVL1Sequence 2
        $.each(data.d.results, function (key, value) {

           //alert("Sequence " + value.Sequence);  

           if(value.Level == 1){               

             if(value.Sequence == 2){
              //listItemInfo +=  value.Title;  
              listItemInfo += "</ul></li><li><a href='#'>" + value.Title + "</a><ul>";                                 
             }             
           }                           
        }); 

        //TopItemID 2 Level 2 LVL2Sequence All
        $.each(data.d.results, function (key, value) {

        if(value.Level == 2){

             if(value.TopItemID == 2){
               listItemInfo += "<li><a href='" + value.Link + "'>" + value.Title + "</a></li>";
               }                  
           }                       
        });    

        listItemInfo += "</ul></li></ul></nav>"
        return $("#w").html(listItemInfo);;
        alert("REST API"); 

    }); 

    failure(function (data) {

      alert("Failure");

    });

});

Here is the Dynamic CSS code, this gives the menu an accordion effect:


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js" type="text/javascript"></script><!-- <script type="text/javascript" language="javascript" charset="utf-8" src="nav.js"></script> --> 
   <!--[if lt IE 9]>
      <script type="text/javascript" src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
      <![endif]-->
         <script>  

  $(document).ready(function(){
  $("#nav > li > a").on("click", function(e){
    if($(this).parent().has("ul")) {
      e.preventDefault();
    }

    if(!$(this).hasClass("open")) {
      // hide any open menus and remove all other classes
      $("#nav li ul").slideUp(350);
      $("#nav li a").removeClass("open");

      // open our new menu and add the open class
      $(this).next("ul").slideDown(350);
      $(this).addClass("open");
    }

    else if($(this).hasClass("open")) {
      $(this).removeClass("open");
      $(this).next("ul").slideUp(350);
    }
  });
});

</script>

Here is the CSS code, this adds the menu style:


<style>

#sideNavBox {DISPLAY: none}
#contentBox {MARGIN-LEFT: 5px}

ol, ul, li {
  padding: 0;
}

menu, nav, section { display: block; }
ol, ul { list-style: none; }

blockquote, q { quotes: none; }
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
strong { font-weight: bold; } 

table { border-collapse: collapse; border-spacing: 0; }

h1 { font-family: 'Merienda', 'Trebuchet MS', Verdana, sans-serif; font-size: 1em; line-height: 1.7em; margin-bottom: 20px; font-weight: bold; letter-spacing: -0.03em; color: #675d90; text-shadow: 2px 2px 0px rgba(255,255,255,0.65); text-align: center; }

#w { display: block; width: 400px; margin: 0 auto; margin-left: 0px; padding-top: 5px; }

/* nav menu styles */

#nav { 
  display: block; 
  width: 150px; 
  margin: 0 auto;
  margin-left: 0px;
  -webkit-box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
  -moz-box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
  box-shadow: 3px 2px 3px rgba(0,0,0,0.7);
}

#nav li { }

#nav > li > a { 
  display: block; 
  padding: 16px 18px;
  font-size: 0.7em;
  font-weight: bold;
  color: #d4d4d4;
  text-decoration: none;
  border-bottom: 1px solid #212121;
  background-color: #343434;
  background: -webkit-gradient(linear, left top, left bottom, from(#948c6f), to(#292929));
  background: -webkit-linear-gradient(top, #595959, #292929);
  background: -moz-linear-gradient(top, #595959, #292929);
  background: -ms-linear-gradient(top, #595959, #292929);
  background: -o-linear-gradient(top, #595959, #292929);
  background: linear-gradient(top, #595959, #292929);
}

#nav > li > a:hover, #nav > li > a.open { 
  color: #e9e9e9;
  border-bottom-color: #384f76;
  background-color: #6985b5;
  background: -webkit-gradient(linear, left top, left bottom, from(#6985b5), to(#456397));
  background: -webkit-linear-gradient(top, #ff8c00, #456397);
  background: -moz-linear-gradient(top, #ff8c00, #456397);
  background: -ms-linear-gradient(top, #ff8c00, #456397);
  background: -o-linear-gradient(top, #ff8c00, #456397);
  background: linear-gradient(top, #ff8c00, #456397);
}

#nav li ul { display: none; background: #f09609; }

#nav li ul li a { 
  display: block; 
  background: none;
  padding: 10px 0px;
  padding-left: 10px;
  font-size: 0.7em;
  text-decoration: none;
  font-weight: bold;
  color: white;
  border-bottom-style: solid;
  border-bottom-color: orange;
  text-shadow: 1px 1px 0px #000;
}

#nav li ul li a:hover {
  background: -webkit-gradient(linear, left top, left bottom, from(#456397), to(#000000));
  background: -webkit-linear-gradient(top, #456397, #000000);
  background: -moz-linear-gradient(top, #456397, #000000);
  background: -ms-linear-gradient(top, #456397, #000000);
  background: -o-linear-gradient(top, #456397, #000000);
  background: linear-gradient(top, #456397, #000000);
}

</style>

And here is the HTML, the Dynamic HTML generated by the REST API code injects into this :

<div id="w"></div>

So here is the problem I'm having. The REST API code works fine and it creates the Dynamic HTML, injecting it into the HTML tag and the Dynamic CSS works fine, but they don't work well together. If I run the REST API code the menu items are created, but the menu isn't dynamic. If, after running the REST API code I changed the tag id from "w" to anything else, for example "w1", the Dynamic CSS code works fine:

Some of the things I've tried include (but are not limited to):

Adding break; to various points in the REST API code

Adding return; to various points in the REST API code

Functions to change the id attribute of the tag - these work but I just can't seem to put them in the right place in the code, no matter where I put them they run before the REST API code and so the REST API doesn't write anything because the id attribute is already changed:

function changeToW1(){
    $('#w').attr('id', 'w1');
    alert("Change W to W1");
 }

function changeToW() {
    $('#w1').attr('id', 'w');
    alert("Change W1 to W");
 }

Nothing works but it seems like somehow the REST API code just won't "let go" of the tag and so the Dynamic CSS code won't attach to the tag.

I know this is somewhat complex but if anyone thinks they can help please provide guidance and code samples, and I'll try your suggestions quickly.

Thanks!

Tom

0

I think the problem is with this $("#nav > li > a").on("click", function(e){ line. JQuery .on() events only work with elements that are on the page at the time the handler is assigned. You could test this with a line right before that like this.

console.log($("#nav > li > a").legnth());

I'm guessing they don't exist. But what you can do is choose something that does exist and then filter/delegate the the click event. For example change your handler assignment to something like this...

$("#w").on('click','#nav > li > a',function(e){...})

You know that your #w id is on the page at that time, so attach the on click handler to that, but then filter all the clicks with the pattern you want. This will work even if those elements are added later.

  • Great answer! I will try that right now and let you know the results. – Tom Molskow Nov 7 at 20:19
  • That worked!!! You're a Super JavaScript Genius!!! – Tom Molskow Nov 7 at 20:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.