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.
- 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.
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!`);
}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.
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.
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)
- Set the display name:
-
`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
- This is where your HTML content goes:
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.
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 fragmentBest 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 elementBest 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.
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:
After saving and reloading WME:
- Look for your tab in the left sidebar - it should appear as "WazeDev"
- Click the tab - you should see "Our First Script!" as the header
-
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.
// ==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.
