How to add external link on top menu?


(indream) #1

i found that Customize can support js,so i add new Site Customization for Header.
like this:
http://forum.makeblock.cc/

<script>
function doLink(){
    var topmenu = document.getElementById("category-filter");
    
    if(topmenu==null){
        return;
    }
    if(topmenu.getElementsByTagName("li").length>5){
        return;
    }
     var button_site=document.createElement("li");
	 button_site.class = "ember-view";
	 var link = document.createElement("a");
	 link.href = "http://mydomain.com";
	 link.title = "My Site";
	 link.appendChild(document.createTextNode("My Site"));
	 button_site.appendChild(link);
    topmenu.appendChild(button_site);
}

window.onload = function() {
    doLink();
   
    var timer = setInterval( doLink , 1000);
    
}
</script>

Adding additional pages and links in header
jQuery in Admin Custom HTML?
What about adding tags in the top menu?
Allowing Admins to place arbitrary links in topnav
What are tag groups?
(raly) #2

Nice work! Thanks for sharing.


(Janek Ostendorf) #3

Works great. I edited one line to change the position of the new menu entry. Change the zero (0) to change the position in the menu. 0 makes it the first entry, 1 the second and so forth.

topmenu.insertBefore(button_site, topmenu.getElementsByTagName("li")[0]);

instead of

topmenu.appendChild(button_site);

(Jake Hower) #4

Strange, this isn’t working for me. Do I need to enable any extra settings in admin?


(Anton) #5

Is there a better than using setInterval?
If I understand correctly, we need a way to append menu items AFTER Ember fills the menu with the standard buttons.


Best way to customize top menu
(Jens Maier) #6

You could try this:

<script>
  Discourse.ExternalNavItem = Discourse.NavItem.extend({
    href : function() {
      return this.get('href');
    }.property('href')
  });

  I18n.translations.de.js.filters.google = { title: "Google", help: "Google" };

  Discourse.NavItem.reopenClass({
    buildList : function(category, args) {
      var list = this._super(category, args);
      list.push(Discourse.ExternalNavItem.create({href: 'http://www.google.com', name: 'google'}));
      return list;
    }
  });
</script>

You’ll have to change the I18n line to match your default locale and replace the various key strings and values.


After add a link to top menu, how can custom the link showed on mobile
Discourse Solved (Accepted answer plugin)
Adding link to blog on main page
How to add a topic in top nav in a specific category?
(Anton) #7

It worked, thanks. There are 2 issues:

  1. The Google menu item is not available on all pages (just clicked on every menu item).
  2. After creating a NavItem, how can I force the new item to be displayed immediately?

(Jens Maier) #8
  1. On which pages specifically is the added item not being shown?
  2. Short answer, you can’t, you need to reload the page in order to refresh the customisation. Long answer, that would require a lot more code and using the message bus to push update events to clients.

(Anton) #9

Okay, I’ve also tried a not-so-beautiful way, but actually all I need is a menu rewrite with using pure JS addition in the admin settings.

alterMenu();
Discourse.NavigationCategoryView.reopen({didInsertElement: function(){
      this._super();
      alterMenu();
      console.log('Caught menu change.');
  } });

The first alterMenu() call changes the menu immediately (simply modifying DOM elements with jQuery).

The problem with this solution is that it does not happen every time I navigate, and sometimes my custom buttons disappears, as altered text of existing buttons does.

To me most of times it was the categories page, but it’s configured to be the home page. However, sometimes it was the only page where the modification took place. So it might be the active page that matters. Probably, there are 2 different menu objects: one for the home page and another for all other pages? I’m not sure, just guessing.

I find it strange as the only thing I want is to change the menu with JS - so why a message bus should be used at all? I want no server involved in the business for this.


(Jens Maier) #10

Then what did you mean with “forcing the new item to be displayed immediately”? The way I understood this was that you wanted to have the item pop up on a user’s nav menu immediately after you add it in the admin interface, even if the user didn’t reload the page.

I tested my script again in my live forum and there it appears to work quite reliably. All pages that have a nav menu also show the Google link, so I’m not sure why it’s not working for you. You did add and enable the customisation, then reload the frontend view, right? If you reload the admin page and browse to the frontend from there, it won’t include your customisation and you won’t see the new button.


(Anton) #11

Okay let me show screenshots to make it more clear.
Also, I’ve checked for js errors in debugger - there are no ones.

After using your code:

Home page

Latest page

The exact code I’m using:

Putting it in the admin settings, before the </body>. Maybe it’s a wrong place - is it?

<script>

$(function(){

Discourse.KozoNavItem = Discourse.NavItem.extend({
  href : function() {
    return this.get('href');
  }.property('href')
});

if (typeof I18n.translations.en != 'undefined') {
  I18n.translations.en.js.filters.kozoportal = {
    title: "Portal",
    help: "The main portal"
  };
}

if (typeof I18n.translations.ru != 'undefined') {
  I18n.translations.ru.js.filters.kozoportal = {
    title: "Портал",
    help: "Главная страница информационного портала"
  };
}

Discourse.NavItem.reopenClass({
  buildList : function(category, args) {
    var list = this._super(category, args);
    list.push(Discourse.KozoNavItem.create({
      href: 'http://kozovodstvo.info',
      name: 'kozoportal'
    }));
    return list;
  }
});

});
</script>

p.s. how can I highligh the code? When I press Ctrl+K, it is just putted in the box, but not highlighted.


(Dave McClure) #12

put your code in a “fenced code block” (surrounded by 2 lines of three backticks)

```
your code here
```

You can also specify the language if you want:

```javascript
your code here
```

(Jens Maier) #13

Well, d’oh. Get rid of the $(function(){ ... } wrapper and put the code into the header via customise, not content.

The code is not modifying the DOM, so there’s no reason to wait until the document has been loaded. Instead, it’s hooking into Ember, which must happen right away, before Ember initially renders the page.


(Anton) #14

Thank you for the explanation, it now works like a charm.
I’ve successfully renamed existing buttons as well by redefining values in the I18n object.


(Cobe) #15

Thank you for your code.
It runs perfect with standard discourse.


(Ryan Hidajat) #16

I try both code and this one said Uncaught “TypeError: Cannot read property ‘js’ of undefined”

it work, after I change de to en :smiley:


(@SenpaiMass) #17

I am getting this error

How do i fix it?


(Ryan Hidajat) #18

can you post your js code?

if you use this line
I18n.translations.de.js.filters.google

Replace de with en
looks like it was translation problem.


(@SenpaiMass) #19
  <script>
  Discourse.ExternalNavItem = Discourse.NavItem.extend({
    href : function() {
      return this.get('href');
    }.property('href')
  });

  I18n.translations.en.js.filters.anime = { title: "Anime", help: "Anime Discussion" };

  Discourse.NavItem.reopenClass({
    buildList : function(category, args) {
      var list = this._super(category, args);
      list.push(Discourse.ExternalNavItem.create({href: 'https://animeforums.me/c/anime', name: 'Anime'}));
      return list;
    }
  });
</script>

This is the code which i am using as of now.


#20

I think it’s the name: 'Anime' that doesn’t work with the uppercase, it should be like this :

<script>
  Discourse.ExternalNavItem = Discourse.NavItem.extend({
    href : function() {
      return this.get('href');
    }.property('href')
  });

  I18n.translations.en.js.filters.anime = { title: "Anime", help: "Anime Discussion" };

  Discourse.NavItem.reopenClass({
    buildList : function(category, args) {
      var list = this._super(category, args);
      if(!category) {
        list.push(Discourse.ExternalNavItem.create({href: '/c/anime', name: 'anime'}));
      }
      return list;
    }
  });
</script>