spatial-navigation-polyfill
Version:
A polyfill for the spatial navigation
188 lines (177 loc) • 7.25 kB
HTML
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="application-name" content="Test Navigation Behavior">
<meta name="author" content="Jeonghee Ahn">
<meta name="description" content="You can directly look into the spatial navigation behavior by yourself using elements you create and arrange. Try to create an element via <b>mouse dragging from empty space</b>, move an element via <b>mouse dragging from inside the element</b>, and delete an element via <b>right click</b>. In order to share a specific arrangement, you can get the snapshot using a set of expressions below as well.">
<link rel="stylesheet" href="spatnav-style.css">
<script src="spatnav-utils.js"></script>
<script src="../../polyfill/spatial-navigation-polyfill.js"></script>
</head>
<body onload="init()">
<div id="container"></div>
<textarea id="nodeInfo"></textarea>
</body>
<style>
#container {
margin-left: 10px;
width: 720px;
height: 400px;
border: 2px #555 solid;;
cursor: crosshair;
background-size: 40px 40px;
background-image: linear-gradient(to right, #777 1px, transparent 1px), linear-gradient(to bottom, #777 1px, transparent 1px);
}
#nodeInfo {
margin-left: 10px;
width: 720px;
height: 150px;
font-family: 'Courier New', Courier, monospace;
}
.b1 {
position: absolute;
box-sizing: border-box;
cursor: pointer;
text-align: center;
font-size: 24px;
}
.b1:focus {
background: #3e6e86;
outline: 0px solid black;
}
.temp_node {
position: absolute;
box-sizing: border-box;
border: 1px #777 dashed;
}
</style>
<script type="text/javascript">
function init() {
const resultSection = document.getElementById('resultSection');
const container = document.getElementById('container');
const nodeInfo = document.getElementById('nodeInfo');
let tempNode = null, selectNode = null;
let startX, startY, mouseStatus;
let childCount = 0;
container.addEventListener("contextmenu", (e) => {
e.preventDefault();
// If the right click is triggered, the target element is removed.
if (e.target.className.split(" ").includes("box")) {
container.removeChild(e.target);
writeCurrentStatus();
}
});
container.addEventListener("mousedown", (e) => {
e.preventDefault();
e.target.focus();
// Save the current mouse position
startX = e.clientX + window.pageXOffset;
startY = e.clientY + window.pageYOffset;
// 1. Select mode
if (e.target.className.split(" ").includes("box")) {
mouseStatus = "select";
selectNode = e.target;
} else { // 2. draw mode
mouseStatus = "down";
if (tempNode == null) {
tempNode = document.createElement("div");
tempNode.className = "temp_node";
tempNode.style.left = startX + "px";
tempNode.style.top = startY + "px";
container.appendChild(tempNode);
}
}
});
container.addEventListener("mouseup", (e) => {
if (mouseStatus == "select") {
// 1. Select mode
selectNode = null;
} else if (tempNode && (mouseStatus == "down" || parseInt(tempNode.style.width) * parseInt(tempNode.style.height) < 300)) {
// 2. Draw mode
// If no mouse move or tiny size, then the element isn't created.
container.removeChild(tempNode);
tempNode = null;
} else if (tempNode) {
tempNode.className = "box b1";
tempNode.tabIndex = 0;
tempNode.innerText = String.fromCharCode(65 + childCount++);
tempNode = null;
}
mouseStatus = "up";
writeCurrentStatus();
});
container.addEventListener("mousemove", (e) => {
if (mouseStatus == "select") {
// 1. Select mode
selectNode.style.left = selectNode.offsetLeft - startX + e.clientX + window.pageXOffset + "px";
selectNode.style.top = selectNode.offsetTop - startY + e.clientY + window.pageYOffset + "px";
startX = e.clientX + window.pageXOffset;
startY = e.clientY + window.pageYOffset;
} else if (tempNode && (mouseStatus == "down" || mouseStatus == "drag")) {
// 2. draw mode
let currentX = e.clientX + window.pageXOffset;
let currentY = e.clientY + window.pageYOffset;
tempNode.style.left = ((currentX - startX < 0) ? currentX : startX) + "px";
tempNode.style.top = ((currentY - startY < 0) ? currentY : startY) + "px";
tempNode.style.width = Math.abs(currentX - startX) + "px";
tempNode.style.height = Math.abs(currentY - startY) + "px";
mouseStatus = "drag";
}
});
function createElement(styleInfo) {
const newElement = document.createElement("div");
({
width: newElement.style.width,
height: newElement.style.height,
left: newElement.style.left,
top: newElement.style.top
} = styleInfo);
newElement.className = "box b1";
newElement.tabIndex = "0";
newElement.innerText = String.fromCharCode(65 + childCount++);
return newElement;
}
function writeCurrentStatus() {
let nodeList = document.getElementsByClassName("box");
let currentStatus = "";
for(node of nodeList) {
const styleObj = {
width: node.style.width,
height: node.style.height,
left: node.style.left,
top: node.style.top
}
currentStatus = currentStatus + "\n" + JSON.stringify(styleObj);
}
nodeInfo.value = currentStatus.trim();
}
function onchangeTextarea() {
const childNodes = container.children;
while(container.children.length) {
container.children[0].remove();
}
let strArray = nodeInfo.value.trim().split("\n");
nodeInfo.value = "";
childCount = 0;
for(let str of strArray) {
if(str.trim().length) {
const styleObj = JSON.parse(str);
container.appendChild(createElement(styleObj));
nodeInfo.value = nodeInfo.value + "\n" + JSON.stringify(styleObj);
}
}
}
nodeInfo.addEventListener('change', onchangeTextarea);
// Add initial element
nodeInfo.value = `{"width":"80px","height":"80px","left":"212px","top":"82px"}
{"width":"80px","height":"80px","left":"292px","top":"82px"}
{"width":"80px","height":"80px","left":"372px","top":"82px"}
{"width":"80px","height":"80px","left":"212px","top":"162px"}
{"width":"80px","height":"80px","left":"292px","top":"162px"}
{"width":"80px","height":"80px","left":"372px","top":"162px"}`;
onchangeTextarea();
}
</script>
</html>