Scroll Range
You can configure the timescale range to control the scroll range of the gantt chart using auto fit mode, margin, and min/max date.
The auto fit mode determines how the timescale range is calculated:
none: The scroll range is determined by minDate and maxDate manually.
project: The scroll range is auto-calculated based on the project tasks. This is the recommended mode.
When autoFitMode is "project", minDate and maxDate are auto-calculated and cannot be set manually.
The margin adds extra ticks before and after the timescale range, using the smallest unit of the current timescale tiers.
Scroll Timescale by Pixel
You can scroll the GanttSheet timescale by pixels, and it will scroll to the nearest tick.
Note: A positive pixel amount will scroll forward, whereas a negative pixel amount will scroll backward.
Scroll Timescale by One Tick
You can scroll the timescale by one tick:
Scroll Timescale by Specified Unit
You can also scroll the timescale by a specific count of units.
The scroll unit is GC.Spread.Sheets.GanttSheet.TimescaleUnit, and it contains:
years
halfYears
quarters
months
thirdsOfMonth
weeks
days
hours
minutes
Note: Timescale will scroll to the nearest tick. Positive pixel amounts will scroll forward, negative pixel amounts will scroll backward.
Scroll Timescale by Specific Date
You can scroll the timescale to a specific date.
Note: The timescale will scroll to the nearest tick.
You can control the location as a percentage of the viewport where the target date want to scroll to.
The default is 0, and will scroll to the target date at the left of viewport.
/*REPLACE_MARKER*/
/*DO NOT DELETE THESE COMMENTS*/
var myTable;
var ganttSheet;
var isForward = false;
window.onload = function() {
var spread = new GC.Spread.Sheets.Workbook(document.getElementById("ss"), { sheetCount: 0 });
initSpread(spread);
initSplitView(spread);
};
function initSpread(spread) {
spread.suspendPaint();
initDataSource(spread);
initGanttSheet(spread);
spread.resumePaint();
}
function initDataSource(spread) {
var tableName = "Gantt_Id";
var baseApiUrl = getBaseApiUrl();
var apiUrl = baseApiUrl + "/" + tableName;
var dataManager = spread.dataManager();
myTable = dataManager.addTable("myTable", {
batch: true,
remote: {
read: {
url: apiUrl
}
},
schema: {
hierarchy: {
type: "Parent",
column: "parentId"
},
columns: {
id: { isPrimaryKey: true },
taskNumber: { dataType: "rowOrder" }
}
}
});
}
function initGanttSheet(spread) {
ganttSheet = spread.addSheetTab(0, "GanttSheet", GC.Spread.Sheets.SheetType.ganttSheet);
var view = myTable.addView("ganttView", [
{ value: "taskNumber", caption: "NO.", width: 60 },
{ value: "name", caption: "Task Name", width: 200 },
{ value: "duration", caption: "Duration", width: 90 },
{ value: "predecessors", caption: "Predecessors", width: 120 }
]);
view.fetch().then(function() {
ganttSheet.bindGanttView(view);
syncRangePanel(ganttSheet);
});
initRangePanel(ganttSheet);
initSidePanel(ganttSheet);
}
function parseUTCDate(str) {
var parts = str.split("/");
return new Date(Date.UTC(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])));
}
function formatDate(date) {
if (!date) return "";
return date.getUTCFullYear() + "/" + (date.getUTCMonth() + 1) + "/" + date.getUTCDate();
}
function syncRangePanel(ganttSheet) {
var timescale = ganttSheet.project.timescale;
var autoFitModeSelect = document.getElementById("auto-fit-mode");
var marginInput = document.getElementById("margin");
var minDateInput = document.getElementById("min-date");
var maxDateInput = document.getElementById("max-date");
autoFitModeSelect.value = timescale.autoFitMode || "none";
marginInput.value = timescale.margin || 0;
minDateInput.value = formatDate(timescale.minDate);
maxDateInput.value = formatDate(timescale.maxDate);
var isProject = autoFitModeSelect.value === "project";
minDateInput.disabled = isProject;
maxDateInput.disabled = isProject;
}
function initRangePanel(ganttSheet) {
var autoFitModeSelect = document.getElementById("auto-fit-mode");
var marginInput = document.getElementById("margin");
var minDateInput = document.getElementById("min-date");
var maxDateInput = document.getElementById("max-date");
var applyButton = document.getElementById("apply-range");
autoFitModeSelect.addEventListener("change", function() {
var isProject = autoFitModeSelect.value === "project";
minDateInput.disabled = isProject;
maxDateInput.disabled = isProject;
});
applyButton.addEventListener("click", function() {
var timescale = ganttSheet.project.timescale;
ganttSheet.suspendPaint();
timescale.autoFitMode = autoFitModeSelect.value;
timescale.margin = Number(marginInput.value) || 0;
if (autoFitModeSelect.value !== "project") {
var minDate = minDateInput.value ? parseUTCDate(minDateInput.value) : undefined;
var maxDate = maxDateInput.value ? parseUTCDate(maxDateInput.value) : undefined;
if (minDate) {
minDate.setUTCHours(8, 0, 0, 0);
timescale.minDate = minDate;
}
if (maxDate) {
maxDate.setUTCHours(17, 0, 0, 0);
timescale.maxDate = maxDate;
}
}
ganttSheet.resumePaint();
syncRangePanel(ganttSheet);
});
}
function initSidePanel(ganttSheet) {
var scrollPixelInput = document.getElementById("scroll-pixel");
var scrollByPixelButton = document.getElementById("scroll-by-pixel");
var isForwardCheckbox = document.getElementById("is-forward");
var scrollOneTickButton = document.getElementById("scroll-one-tick");
var scrollUnitSelect = document.getElementById("scroll-unit");
var scrollCountInput = document.getElementById("scroll-count");
var scrollByUnitButton = document.getElementById("scroll-by-unit");
var scrollDateInput = document.getElementById("scroll-date");
var scrollPercentInput = document.getElementById("scroll-percent");
var scrollToButton = document.getElementById("scroll-to");
var currentDate = new Date();
scrollDateInput.value = `${currentDate.getFullYear()}/${currentDate.getMonth() + 1}/${currentDate.getDate()}`;
scrollByPixelButton.addEventListener("click", function() {
var scrollPixel = Number(scrollPixelInput.value);
ganttSheet.project.timescale.scroll(scrollPixel);
});
isForwardCheckbox.addEventListener("click", function() {
if (isForwardCheckbox.classList.contains("active")) {
isForwardCheckbox.classList.remove("active");
isForward = false;
isForwardCheckbox.innerHTML = "Backward";
} else {
isForwardCheckbox.classList.add("active");
isForward = true;
isForwardCheckbox.innerHTML = "Forward";
}
});
scrollOneTickButton.addEventListener("click", function() {
ganttSheet.project.timescale.scrollOneTick(isForward);
});
scrollByUnitButton.addEventListener("click", function() {
var scrollCount = Number(scrollCountInput.value);
var scrollUnit = Number(scrollUnitSelect.value);
ganttSheet.project.timescale.scrollBy(scrollCount, scrollUnit);
});
scrollToButton.addEventListener("click", function() {
var scrollDate = new Date(scrollDateInput.value);
var scrollPercent = Number(scrollPercentInput.value);
ganttSheet.project.timescale.scrollTo(scrollDate, scrollPercent);
});
}
function getBaseApiUrl() {
return window.location.href.match(/http.+spreadjs\/demos\//)[0] + 'server/api';
}
function initSplitView(spread) {
var host = document.getElementById("split-view");
var content = host.getElementsByClassName("split-content")[0];
var panel = host.getElementsByClassName("split-panel")[0];
new SplitView({
host: host,
content: content,
panel: panel,
refreshContent: function() {
spread.refresh();
}
});
}
<!doctype html>
<html style="height:100%;font-size:14px;">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css">
<link rel="stylesheet" type="text/css" href="$DEMOROOT$/spread/source/splitView/splitView.css">
<!-- Promise Polyfill for IE, https://www.npmjs.com/package/promise-polyfill -->
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
<script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js" type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/splitView/splitView.js" type="text/javascript"></script>
<script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="split-view">
<div id="ss" class="split-content"></div>
<div class="split-panel">
<div class="option-row option-title">
Scroll range settings.
</div>
<div class="option-block">
<div class="option-row selection-box">
<label for="auto-fit-mode">Auto Fit Mode</label>
<select id="auto-fit-mode">
<option value="none">None</option>
<option value="project">Project</option>
</select>
</div>
<div class="option-row input-box">
<label for="margin">Margin</label>
<input type="text" id="margin" value="0" />
<div class="option-info valid">* valid value: integer >= 0</div>
</div>
<div class="option-row input-box">
<label for="min-date">Min Date</label>
<input type="text" id="min-date" />
<div class="option-info valid">* format: 2022/1/3</div>
</div>
<div class="option-row input-box">
<label for="max-date">Max Date</label>
<input type="text" id="max-date" />
<div class="option-info valid">* format: 2022/1/3</div>
</div>
<div class="option-row">
<input type="button" id="apply-range" class="option-button" value="Set Scroll Range" />
</div>
</div>
<div class="option-row option-title">
Scroll the ganttsheet chart area.
</div>
<div class="option-block">
<div class="option-row input-box">
<label for="scroll-pixel">Scroll Pixel</label>
<input type="text" id="scroll-pixel" value="50" />
<div class="option-info valid">* valid value: integer, could be negative</div>
</div>
<div class="option-row">
<input type="button" id="scroll-by-pixel" class="option-button" value="Scroll By Pixel" />
</div>
</div>
<div class="option-block">
<div class="option-row">
<label class="option-checkbox" id="is-forward">Backward</label>
<div class="option-info">* checkbox toggle between "Forward" and "Backward"</div>
</div>
<div class="option-row">
<input type="button" id="scroll-one-tick" class="option-button" value="Scroll One Tick" />
</div>
</div>
<div class="option-block">
<div class="option-row selection-box">
<label for="scroll-unit">Scroll Unit</label>
<select id="scroll-unit">
<option value="1">years</option>
<option value="2">halfYears</option>
<option value="3">quarters</option>
<option value="4">months</option>
<option value="5">thirdsOfMonth</option>
<option value="6">weeks</option>
<option value="7" selected>days</option>
<option value="8">hours</option>
<option value="9">minutes</option>
</select>
</div>
<div class="option-row input-box">
<label for="scroll-count">Scroll Count</label>
<input type="text" id="scroll-count" value="1" />
<div class="option-info valid">* valid value: integer, could be negative</div>
</div>
<div class="option-row">
<input type="button" id="scroll-by-unit" class="option-button" value="Scroll By Unit" />
</div>
</div>
<div class="option-block">
<div class="option-row input-box">
<label for="scroll-date">Scroll Date</label>
<input type="text" id="scroll-date" />
<div class="option-info valid">* valid value: format 2022/1/3 or 2022/1/3 12:00:00</div>
</div>
<div class="option-row input-box">
<label for="scroll-percent">Percent Of Viewport</label>
<input type="text" id="scroll-percent" value="0.5" />
<div class="option-info valid">* valid value: number between 0~1</div>
</div>
<div class="option-row">
<input type="button" id="scroll-to" class="option-button" value="Scroll To Date" />
</div>
</div>
</div>
</div>
</html>
.option-block {
background: #fff;
padding: 8px;
margin: 12px 0;
border-radius: 4px;
border: 1px dashed #82bc00;
box-shadow: 0px 0 6px 0 rgba(0,0,0,0.1);
}
.option-block.toggle {
border: 1px dotted #f7a711;
}
.option-row {
font-size: 14px;
box-sizing: border-box;
padding: 4px 0;
}
.option-title {
font-weight: bold;
color: #656565;
}
.option-info {
font-size: 12px;
color: #919191;
margin-top: 6px;
font-weight: normal;
}
.option-info.valid {
color: #82bc00;
}
.option-info.toggle {
color: #f7a711;
}
.option-button {
width: 100%;
padding: 0;
line-height: 20px;
background: #82bc00;
color: #fff;
transition: 0.3s;
cursor: pointer;
outline: none;
border-radius: 4px;
box-sizing: border-box;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
border: none;
}
.option-button:hover {
background: #82bc00;
color: #fff;
box-shadow: 0 3px 8px 0 rgba(0,0,0,0.4);
}
.option-checkbox {
background: #fff;
border: 1px dashed #f7a711;
color: #f7a711;
padding: 2px 4px;
transition: 0.3s;
box-sizing: border-box;
cursor: pointer;
}
.option-checkbox.active {
color: #fff;
background: #f7a711;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
border-radius: 4px;
}
.selection-box {
position: relative;
}
.selection-box > select {
text-align: left;
width: 100%;
height: 20px;
padding: 0;
line-height: 20px;
background: transparent;
border: none;
border-bottom: 2px solid #656565;
color: #656565;
transition: 0.3s;
cursor: pointer;
outline: none;
box-sizing: border-box;
}
.selection-box > select > option {
background: white;
}
.selection-box > select:focus {
border-bottom: 2px solid #82bc00;
color: #82bc00;
box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3);
}
.selection-box > label {
position: absolute;
cursor: pointer;
font-size: 12px;
color: #fff;
background: #656565;
padding: 0 4px;
right: 0;
top: 6px;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
}
.input-box {
position: relative;
}
.input-box > input[type=text] {
width: 100%;
background: transparent;
border: none;
color: #656565;
border-bottom: 2px solid #656565;
outline: none;
box-sizing: border-box;
transition: 0.3s;
}
.input-box > input[type=text]:focus {
color: #82bc00;
border-bottom: 2px solid #82bc00;
}
.input-box > label {
cursor: pointer;
position: absolute;
right: 0;
top: 5px;
font-size: 12px;
color: #fff;
background: #656565;
padding: 0 4px;
box-shadow: 0 1px 4px 0 rgba(0,0,0,0.3);
}