2.1 Sciter-JS 语法概述
2.1.1 语法特性
Sciter-JS 基于标准 JavaScript ES6+ 语法,同时提供了一些扩展特性:
graph TD
A[Sciter-JS 语法] --> B[标准 JavaScript]
A --> C[Sciter 扩展]
B --> D[ES6+ 特性]
B --> E[异步编程]
C --> F[DOM 扩展]
C --> G[系统 API]
C --> H[原生绑定]
2.1.2 基本数据类型
// 基本数据类型
let number = 42;
let string = "Hello Sciter";
let boolean = true;
let array = [1, 2, 3];
let object = { name: "Sciter", version: "5.0" };
// Sciter 特有类型
let element = document.querySelector("#myElement");
let graphics = new Graphics(element);
let image = Image.load("path/to/image.png");
2.1.3 变量声明与作用域
// 变量声明
let localVar = "局部变量";
const CONSTANT = "常量";
var globalVar = "全局变量"; // 不推荐
// 块级作用域
{
let blockScoped = "块级作用域";
console.log(blockScoped); // 可访问
}
// console.log(blockScoped); // 错误:无法访问
// 函数作用域
function myFunction() {
let functionScoped = "函数作用域";
return functionScoped;
}
2.2 函数与对象
2.2.1 函数定义
// 函数声明
function greet(name) {
return `Hello, ${name}!`;
}
// 函数表达式
const greetExpression = function(name) {
return `Hello, ${name}!`;
};
// 箭头函数
const greetArrow = (name) => `Hello, ${name}!`;
// 异步函数
async function fetchData(url) {
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.error("获取数据失败:", error);
throw error;
}
}
2.2.2 对象操作
// 对象字面量
const user = {
name: "张三",
age: 25,
email: "zhangsan@example.com",
// 方法定义
greet() {
return `你好,我是 ${this.name}`;
},
// 计算属性
get fullInfo() {
return `${this.name} (${this.age}岁)`;
},
set updateAge(newAge) {
if (newAge > 0) {
this.age = newAge;
}
}
};
// 对象解构
const { name, age } = user;
console.log(name, age);
// 对象扩展
const extendedUser = {
...user,
city: "北京",
country: "中国"
};
2.2.3 类与继承
// 基类定义
class Component {
constructor(element) {
this.element = element;
this.initialized = false;
}
init() {
if (!this.initialized) {
this.setupEvents();
this.initialized = true;
}
}
setupEvents() {
// 子类实现
}
destroy() {
this.element = null;
this.initialized = false;
}
}
// 继承
class Button extends Component {
constructor(element, options = {}) {
super(element);
this.options = {
clickable: true,
...options
};
}
setupEvents() {
if (this.options.clickable) {
this.element.on("click", this.handleClick.bind(this));
}
}
handleClick(event) {
console.log("按钮被点击", event);
this.element.dispatchEvent(new CustomEvent("button-click", {
detail: { button: this }
}));
}
}
2.3 DOM 基础操作
2.3.1 元素选择
// 基本选择器
const elementById = document.getElementById("myId");
const elementsByClass = document.getElementsByClassName("myClass");
const elementsByTag = document.getElementsByTagName("div");
// CSS 选择器
const singleElement = document.querySelector(".container > .item:first-child");
const multipleElements = document.querySelectorAll(".item[data-active='true']");
// Sciter 扩展选择器
const elementByPath = document.selectByPath("body > div:nth-child(2) > span");
const elementsByXPath = document.selectAllByXPath("//div[@class='item']");
// 相对选择
const parent = element.parentElement;
const children = element.children;
const siblings = element.parentElement.children;
const nextSibling = element.nextElementSibling;
const prevSibling = element.previousElementSibling;
2.3.2 元素属性操作
// 属性读写
const element = document.querySelector("#myElement");
// 标准属性
element.id = "newId";
element.className = "new-class";
element.textContent = "新文本内容";
element.innerHTML = "<span>HTML 内容</span>";
// 自定义属性
element.setAttribute("data-value", "123");
const dataValue = element.getAttribute("data-value");
element.removeAttribute("data-old");
// 属性检查
if (element.hasAttribute("data-value")) {
console.log("元素具有 data-value 属性");
}
// 数据集操作
element.dataset.userId = "user123";
element.dataset.status = "active";
console.log(element.dataset.userId); // "user123"
// 样式操作
element.style.color = "red";
element.style.backgroundColor = "#f0f0f0";
element.style.setProperty("--custom-var", "value");
// CSS 类操作
element.classList.add("active", "highlighted");
element.classList.remove("inactive");
element.classList.toggle("visible");
if (element.classList.contains("active")) {
console.log("元素包含 active 类");
}
2.3.3 元素创建与插入
// 创建元素
const newDiv = document.createElement("div");
newDiv.className = "new-item";
newDiv.textContent = "新创建的元素";
// 创建文本节点
const textNode = document.createTextNode("纯文本节点");
// 创建文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 5; i++) {
const item = document.createElement("li");
item.textContent = `项目 ${i + 1}`;
fragment.appendChild(item);
}
// 插入元素
const container = document.querySelector(".container");
// 末尾插入
container.appendChild(newDiv);
container.appendChild(fragment);
// 指定位置插入
const referenceElement = container.firstElementChild;
container.insertBefore(newDiv, referenceElement);
// 相邻插入
referenceElement.insertAdjacentElement("beforebegin", newDiv);
referenceElement.insertAdjacentElement("afterbegin", newDiv);
referenceElement.insertAdjacentElement("beforeend", newDiv);
referenceElement.insertAdjacentElement("afterend", newDiv);
// HTML 字符串插入
container.insertAdjacentHTML("beforeend", "<p>通过 HTML 字符串插入</p>");
2.3.4 元素删除与替换
// 删除元素
const elementToRemove = document.querySelector(".to-remove");
if (elementToRemove) {
elementToRemove.remove(); // 现代方法
// 或者
elementToRemove.parentElement.removeChild(elementToRemove); // 传统方法
}
// 清空容器
const container = document.querySelector(".container");
container.innerHTML = ""; // 简单但可能有内存泄漏
// 或者
while (container.firstChild) {
container.removeChild(container.firstChild);
}
// 替换元素
const oldElement = document.querySelector(".old-element");
const newElement = document.createElement("div");
newElement.className = "new-element";
newElement.textContent = "替换后的元素";
oldElement.parentElement.replaceChild(newElement, oldElement);
2.4 Sciter 特有的 DOM 扩展
2.4.1 元素状态管理
// 元素状态
const button = document.querySelector("button");
// 设置状态
button.state.disabled = true;
button.state.checked = false;
button.state.expanded = true;
// 读取状态
if (button.state.disabled) {
console.log("按钮已禁用");
}
// 状态变化监听
button.on("statechange", function(event) {
console.log("状态变化:", event.detail);
});
// 自定义状态
button.state.custom = "myValue";
button.setAttribute(":custom", "myValue"); // 等效写法
2.4.2 元素几何信息
const element = document.querySelector(".my-element");
// 获取尺寸和位置
const rect = element.getBoundingClientRect();
console.log("位置:", rect.left, rect.top);
console.log("尺寸:", rect.width, rect.height);
// Sciter 扩展的几何信息
const box = element.box;
console.log("内容区域:", box.content);
console.log("内边距区域:", box.padding);
console.log("边框区域:", box.border);
console.log("外边距区域:", box.margin);
// 滚动信息
console.log("滚动位置:", element.scrollLeft, element.scrollTop);
console.log("滚动尺寸:", element.scrollWidth, element.scrollHeight);
console.log("客户端尺寸:", element.clientWidth, element.clientHeight);
2.4.3 元素动画
// CSS 过渡动画
const element = document.querySelector(".animated-element");
// 设置过渡
element.style.transition = "all 0.3s ease";
element.style.transform = "translateX(100px)";
// 监听动画事件
element.on("transitionend", function(event) {
console.log("过渡动画结束", event.propertyName);
});
// Sciter 动画 API
element.animate({
transform: "scale(1.2) rotate(45deg)",
opacity: 0.8
}, {
duration: 500,
easing: "ease-in-out",
fill: "forwards"
}).then(() => {
console.log("动画完成");
});
// 关键帧动画
element.animate([
{ transform: "translateX(0px)", opacity: 1 },
{ transform: "translateX(50px)", opacity: 0.5 },
{ transform: "translateX(100px)", opacity: 1 }
], {
duration: 1000,
iterations: 2,
direction: "alternate"
});
2.5 实践练习
2.5.1 动态列表管理器
class ListManager {
constructor(containerSelector) {
this.container = document.querySelector(containerSelector);
this.items = [];
this.init();
}
init() {
this.createControls();
this.setupEvents();
}
createControls() {
const controls = document.createElement("div");
controls.className = "list-controls";
controls.innerHTML = `
<input type="text" id="itemInput" placeholder="输入项目内容">
<button id="addBtn">添加</button>
<button id="clearBtn">清空</button>
`;
const list = document.createElement("ul");
list.className = "dynamic-list";
this.container.appendChild(controls);
this.container.appendChild(list);
this.input = controls.querySelector("#itemInput");
this.addBtn = controls.querySelector("#addBtn");
this.clearBtn = controls.querySelector("#clearBtn");
this.list = list;
}
setupEvents() {
this.addBtn.on("click", () => this.addItem());
this.clearBtn.on("click", () => this.clearItems());
this.input.on("keypress", (e) => {
if (e.key === "Enter") {
this.addItem();
}
});
}
addItem() {
const text = this.input.value.trim();
if (!text) return;
const item = {
id: Date.now(),
text: text,
created: new Date()
};
this.items.push(item);
this.renderItem(item);
this.input.value = "";
this.input.focus();
}
renderItem(item) {
const li = document.createElement("li");
li.className = "list-item";
li.dataset.id = item.id;
li.innerHTML = `
<span class="item-text">${item.text}</span>
<span class="item-time">${item.created.toLocaleTimeString()}</span>
<button class="remove-btn">删除</button>
`;
// 添加删除事件
const removeBtn = li.querySelector(".remove-btn");
removeBtn.on("click", () => this.removeItem(item.id));
// 添加动画效果
li.style.opacity = "0";
li.style.transform = "translateY(-20px)";
this.list.appendChild(li);
// 触发动画
requestAnimationFrame(() => {
li.style.transition = "all 0.3s ease";
li.style.opacity = "1";
li.style.transform = "translateY(0)";
});
}
removeItem(id) {
const itemElement = this.list.querySelector(`[data-id="${id}"]`);
if (itemElement) {
itemElement.style.transition = "all 0.3s ease";
itemElement.style.opacity = "0";
itemElement.style.transform = "translateX(100%)";
setTimeout(() => {
itemElement.remove();
}, 300);
}
this.items = this.items.filter(item => item.id !== id);
}
clearItems() {
this.items = [];
this.list.innerHTML = "";
}
getItems() {
return [...this.items];
}
}
// 使用示例
document.ready = function() {
const listManager = new ListManager(".app-container");
// 添加一些初始数据
setTimeout(() => {
listManager.input.value = "示例项目 1";
listManager.addItem();
listManager.input.value = "示例项目 2";
listManager.addItem();
}, 500);
};
2.5.2 表单验证器
class FormValidator {
constructor(formSelector) {
this.form = document.querySelector(formSelector);
this.rules = new Map();
this.errors = new Map();
this.init();
}
init() {
this.setupEvents();
}
setupEvents() {
this.form.on("submit", (e) => {
e.preventDefault();
this.validate();
});
// 实时验证
this.form.on("input", (e) => {
if (e.target.matches("input, textarea, select")) {
this.validateField(e.target);
}
});
}
addRule(fieldName, validator, message) {
if (!this.rules.has(fieldName)) {
this.rules.set(fieldName, []);
}
this.rules.get(fieldName).push({ validator, message });
return this;
}
validateField(field) {
const fieldName = field.name;
const value = field.value;
const rules = this.rules.get(fieldName) || [];
this.errors.delete(fieldName);
for (const rule of rules) {
if (!rule.validator(value, field)) {
this.errors.set(fieldName, rule.message);
break;
}
}
this.updateFieldUI(field);
return !this.errors.has(fieldName);
}
validate() {
const fields = this.form.querySelectorAll("input, textarea, select");
let isValid = true;
fields.forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
if (isValid) {
this.onSuccess();
} else {
this.onError();
}
return isValid;
}
updateFieldUI(field) {
const fieldName = field.name;
const errorElement = this.form.querySelector(`[data-error-for="${fieldName}"]`);
if (this.errors.has(fieldName)) {
field.classList.add("error");
field.classList.remove("valid");
if (errorElement) {
errorElement.textContent = this.errors.get(fieldName);
errorElement.style.display = "block";
}
} else {
field.classList.remove("error");
field.classList.add("valid");
if (errorElement) {
errorElement.style.display = "none";
}
}
}
onSuccess() {
console.log("表单验证成功");
// 提交表单数据
const formData = new FormData(this.form);
console.log("表单数据:", Object.fromEntries(formData));
}
onError() {
console.log("表单验证失败", this.errors);
}
// 预定义验证器
static validators = {
required: (value) => value.trim() !== "",
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
minLength: (min) => (value) => value.length >= min,
maxLength: (max) => (value) => value.length <= max,
pattern: (regex) => (value) => regex.test(value),
number: (value) => !isNaN(value) && value !== "",
range: (min, max) => (value) => {
const num = parseFloat(value);
return !isNaN(num) && num >= min && num <= max;
}
};
}
// 使用示例
document.ready = function() {
const validator = new FormValidator("#myForm");
validator
.addRule("username", FormValidator.validators.required, "用户名不能为空")
.addRule("username", FormValidator.validators.minLength(3), "用户名至少3个字符")
.addRule("email", FormValidator.validators.required, "邮箱不能为空")
.addRule("email", FormValidator.validators.email, "邮箱格式不正确")
.addRule("age", FormValidator.validators.number, "年龄必须是数字")
.addRule("age", FormValidator.validators.range(1, 120), "年龄必须在1-120之间");
};
2.6 本章小结
2.6.1 核心概念
- 语法基础:Sciter-JS 基于标准 JavaScript,提供额外的 DOM 和系统 API
- DOM 操作:元素选择、属性操作、内容修改、结构变更
- 事件处理:标准事件模型加上 Sciter 特有的扩展
- 状态管理:元素状态的读取和设置
2.6.2 技术要点
- 选择器优化:合理使用不同类型的选择器提高性能
- 内存管理:及时清理事件监听器和引用
- 动画性能:使用 CSS 过渡和 transform 属性
- 代码组织:使用类和模块化方式组织代码
2.6.3 最佳实践
- 使用现代 JavaScript 语法(ES6+)
- 避免直接操作 innerHTML,优先使用 DOM API
- 合理使用事件委托减少内存占用
- 编写可复用的组件类
2.6.4 下一章预告
下一章将深入学习 事件处理与用户交互,包括: - 事件模型和事件流 - 鼠标、键盘、触摸事件 - 自定义事件和事件委托 - 用户交互模式和最佳实践
通过本章的学习,你已经掌握了 Sciter-JS 的基础语法和 DOM 操作技能,为后续的高级功能学习打下了坚实基础。