Creating our tab - WazeDev/WazeDev.github.io GitHub Wiki

Now that your script loads properly, let's create a user interface where users can interact with your script. The WME SDK provides several UI locations, but the sidebar tab is the most common and user-friendly option.

Why Use Sidebar Tabs?

  • Familiar Location: Users expect to find script settings in the sidebar
  • Non-Intrusive: Doesn't interfere with the main map editing workflow
  • Organized: Each script gets its own dedicated space
  • SDK Support: Built-in methods handle all the complex integration

Now let's create your first sidebar tab using the SDK's registerScriptTab method.

Setting Up the Tab

First, modify the init() method to register your sidebar tab and create an empty main() function:

function init() {
    console.log(`${SCRIPT_NAME}: Script initialized successfully!`);

    // Build HTML content for our tab
    const section = document.createElement('div');
    section.innerHTML = `
        <div>
            <h2>Our First Script!</h2>
        </div>
    `;

    // Register the script tab using the SDK
    wmeSDK.Sidebar.registerScriptTab()
        .then(({ tabLabel, tabPane }) => {
            tabLabel.textContent = 'WazeDev';
            tabLabel.title = SCRIPT_NAME;
            tabPane.appendChild(section);

            // Initialize the main() function after tab is created
            main();
        })
        .catch((error) => {
            console.error(`${SCRIPT_NAME}: Error creating script tab`, error);
        });
}

function main() {
    // Settings initialization code will go here
    console.log(`${SCRIPT_NAME}: Settings initialized!`);
}

Understanding the Code Structure

HTML Content Creation

const section = document.createElement('div');
section.innerHTML = `
    <div>
        <h2>Our First Script!</h2>
    </div>
`;

This creates our tab's content using a template literal (notice the backticks ` ). Template literals allow multi-line strings and variable interpolation, making HTML creation cleaner than string concatenation.

Promise-Based Tab Registration

wmeSDK.Sidebar.registerScriptTab()
    .then(({ tabLabel, tabPane }) => {
        // Success: Tab created successfully
    })
    .catch((error) => {
        // Error: Something went wrong
    });

Why use Promises here? Tab creation involves DOM manipulation and potential conflicts with other scripts. The Promise pattern ensures your script handles both success and failure scenarios gracefully.

Tab Components Explained

When registerScriptTab() succeeds, it provides two DOM elements through destructuring assignment:

  • tabLabel - The clickable tab button in the sidebar

    • Set the display name: tabLabel.textContent = 'WazeDev'
    • Add hover tooltip: tabLabel.title = SCRIPT_NAME
    • Keep names short (8-12 characters work best)
  • `tabPane - The content area that appears when users click your tab

    • This is where your HTML content goes: tabPane.appendChild(section)
    • Can contain any standard HTML: forms, buttons, lists, etc.
    • Styled automatically to match WME's appearance

There are multiple ways to define the HTML for the page - you are not locked into doing it the same as the above example, it is just a way to define the tab HTML.

Alternative HTML Creation Methods

While we used createElement() and innerHTML, you have several options:

Method 1: Template Literals (Our Example)

const section = document.createElement('div');
section.innerHTML = `<div><h2>Our First Script!</h2></div>`;

Best for: Complex HTML with multiple elements

Method 2: Pure DOM Methods

const section = document.createElement('div');
const wrapper = document.createElement('div');
const header = document.createElement('h2');
header.textContent = 'Our First Script!';
wrapper.appendChild(header);
section.appendChild(wrapper);

Best for: Simple structures or when you need element references

Method 3: Document Fragments

const fragment = document.createDocumentFragment();
// Build multiple elements, then append fragment

Best for: Performance-critical scenarios with many elements

Method 4: jQuery

const $section = $('<div>').html(`
    <div>
        <h2>Our First Script!</h2>
        <p>Built with jQuery!</p>
    </div>
`);

tabPane.appendChild($section[0]); // Convert jQuery object to DOM element

Best for: Developers familiar with jQuery syntax (shorter, more readable code)

In the future choose the method that feels most comfortable for your coding style. But we will use Method 1: Template Literals in this exsample.

Key Takeaways

This example demonstrates several important WME scripting concepts:

  • Promise handling for async SDK operations
  • DOM manipulation using modern JavaScript
  • Error handling with try/catch patterns
  • SDK integration using the sidebar API

These patterns will serve as the foundation for more complex script features.

Your tab should look like the following:

Testing Your Tab

After saving and reloading WME:

  1. Look for your tab in the left sidebar - it should appear as "WazeDev"
  2. Click the tab - you should see "Our First Script!" as the header
  3. Check the console - you should see: WazeDev First Script: Settings initialized!

Troubleshooting:

  • Tab doesn't appear: Check console for errors in the registerScriptTab() call
  • Content is blank: Verify your HTML syntax in the template literal
  • Tab appears but clicking does nothing: Check that tabPane.appendChild(section) executed successfully

💡 Tip: Use your browser's Element Inspector (F12 → Elements) to examine the generated HTML structure and debug styling issues.

Complete Script So Far:

// ==UserScript==
// @name         WazeDev First Script              // Display name in Tampermonkey dashboard
// @namespace    http://tampermonkey.net/          // Unique identifier (use your GreasyFork profile for updates)
// @version      0.1                               // Version number for update tracking
// @description  Learning to script!               // Brief description of functionality
// @author       You                               // Your name or username
// @include      https://beta.waze.com/*           // Run on beta WME (testing environment)
// @include      https://www.waze.com/editor*      // Run on production WME
// @include      https://www.waze.com/*/editor*    // Run on localized WME URLs
// @exclude      https://www.waze.com/user/editor* // Don't run on user profile pages
// @exclude      https://www.waze.com/editor/sdk/* // Don't run on SDK documentation
// @grant        none                              // No special permissions needed
// ==/UserScript==

(function() {
    'use strict';

    const SCRIPT_NAME = GM_info.script.name; // Get the Script name from the @name tag of the UserScript Meta Keys
    let wmeSDK; // Declare wmeSDK globally

    if (window.SDK_INITIALIZED) {
    console.log(`${SCRIPT_NAME}: SDK initialized...`);
    
    window.SDK_INITIALIZED.then(bootstrap).catch((err) => {
      console.error(`${SCRIPT_NAME}: SDK initialization failed`, err);
    });
  } else {
    console.warn(`${SCRIPT_NAME}: SDK_INITIALIZED is undefined`);
  }

  function bootstrap() {
    try {
      wmeSDK = getWmeSdk({
        scriptId: SCRIPT_NAME.replaceAll(' ', ''),
        scriptName: SCRIPT_NAME,
      });

      Promise.all([wmeReady()])
        .then(() => {
          console.log(`${SCRIPT_NAME}: All dependencies are ready.`);
          init();
        })
        .catch((error) => {
          console.error(`${SCRIPT_NAME}: Error during bootstrap`, error);
        });
    } catch (error) {
      console.error(`${SCRIPT_NAME}: Failed to initialize SDK`, error);
    }
  }

  function wmeReady() {
    return new Promise((resolve) => {
      if (wmeSDK.State.isReady()) {
        console.log(`${SCRIPT_NAME}: WME is ready.`);
        resolve();
      } else {
        console.log(`${SCRIPT_NAME}: Waiting for WME to be ready...`);
        wmeSDK.Events.once({ eventName: 'wme-ready' })
          .then(() => {
            console.log(`${SCRIPT_NAME}: WME is ready now.`);
            resolve();
          })
          .catch((error) => {
            console.error(`${SCRIPT_NAME}: Error while waiting for WME to be ready:`, error);
          });
      }
    });
  }

  function init() {
    console.log(`${SCRIPT_NAME}: Script initialized successfully!`);

    // Build HTML content for our tab
    const section = document.createElement('div');
    section.innerHTML = `
        <div>
            <h2>Our First Script!</h2>
        </div>
    `;

    // Register the script tab using the SDK
    wmeSDK.Sidebar.registerScriptTab()
        .then(({ tabLabel, tabPane }) => {
            tabLabel.textContent = 'WazeDev';
            tabLabel.title = SCRIPT_NAME;
            tabPane.appendChild(section);

            // Initialize the main() function after tab is created
            main();
        })
        .catch((error) => {
            console.error(`${SCRIPT_NAME}: Error creating script tab`, error);
        });
}

function main() {
    // Settings initialization code will go here
    console.log(`${SCRIPT_NAME}: Settings initialized!`);
}

})();

In the next section we will start interacting with the Waze SDK and display some data in our tab.

⚠️ **GitHub.com Fallback** ⚠️