1. 程式人生 > 其它 >【JS從入門到精通】18-定時呼叫與延時呼叫

【JS從入門到精通】18-定時呼叫與延時呼叫

定時呼叫與延時呼叫

筆記來源尚矽谷最新版JavaScript基礎全套教程完整版(140集實戰教學,JS從入門到精通)_嗶哩嗶哩_bilibili

目錄

定時呼叫與延時呼叫

1、定時呼叫

JS 的程式的執行速度是非常非常快的如果希望一段程式,可以每間隔一段時間執行一次,可以使用定時呼叫

setInterval()

定時呼叫,可以將一個函式,每隔一段時間執行一次

引數:

  1. 回撥函式,該函式會每隔一段時間被呼叫一次
  2. 每次呼叫間隔的時間,單位是毫秒

返回值:返回一個Number型別的資料,這個數字用來作為定時器的唯一標識

var num = 1;
info = document.getElementById("info");
setInterval(function(){
    info.innerHTML = num++;
}, 1000);
setInterval(function(){
    info.innerHTML = num++;
}, 100);

clearInterval()

可以用來關閉一個定時器,方法中需要一個定時器的標識作為引數,這樣將關閉標識對應的定時器

var timer = setInterval(function(){
    info.innerHTML = num++;
    if(num > 100){
        clearInterval(timer);
    }
}, 10);

<練習1:定時圖片切換>

HTML 程式碼

<img src="img/1.jpg" id="img" /><br>
<button type="button" id="btnStart">開始</button>
<button type="button" id="btnEnd">結束</button>

JS 程式碼

var btnStart = document.getElementById("btnStart");
var btnEnd = document.getElementById("btnEnd");
var img = document.getElementById("img");
// 設定輪播圖片陣列
var imgArr = ["img/1.jpg", "img/2.jpg", "img/3.jpg", "img/4.jpg", "img/5.jpg"];
// 設定輪播圖片索引
var index = 0;
// 為開始按鈕繫結單擊響應函式
var timer;
btnStart.onclick = function() {
    // 清除上一個定時器
    clearInterval(timer);
    // 設定定時器
    timer = setInterval(function() {
        // 切換圖片
        img.src = imgArr[index++];
        // 判斷索引是否超過最大索引
        index %= imgArr.length;
    }, 500);
};
// 為結束按鈕繫結單擊響應函式
btnEnd.onclick = function() {
    clearInterval(timer);
};

效果

注意點一:迴圈切換圖片

當索引超過最大索引時,需要將索引重置,以達到輪播圖片之目的

// if(index >= imgArr.length){
//     index = 0;
// }
index %= imgArr.length;

注意點二:不點選開始,而直接點選結束

clearInterval()可以接收任意引數

  • 如果引數是一個有效的定時器的標識,則停止對應的定時器
  • 如果引數不是一個有效的標識,則什麼也不做

即使沒有點開始,timer 為 undefined 也不會報錯,可以放心大膽的去使用

注意點三:多次點選開始按鈕導致切換速度過快問題

目前,我們每點選一次按鈕,就會開啟一個定時器,點選多次就會開啟多個定時器

這就導致圖片的切換速度過快,並且我們只能關閉最後一次開啟的定時器

在開啟定時器之前,需要將當前元素上的其他定時器關閉

<練習2:div移動優化>

// 定義速度
var speed = 20;
// 定義方向
var direct = 0;
// 定時器只控制方向
setInterval(function(){
    switch (direct) {
        case 37:
            box1.style.left = box1.offsetLeft - speed + "px";
            break;
        case 38:
            box1.style.top = box1.offsetTop - speed + "px";
            break;
        case 39:
            box1.style.left = box1.offsetLeft + speed + "px";
            break;
        case 40:
            box1.style.top = box1.offsetTop + speed + "px";
            break;
        default:
            break;
    }
}, 50);
// 鍵盤按下控制速度
var box1 = document.getElementById("box1");
document.onkeydown = function(event) {
    event = event || window.event;
    // 修改速度
    speed = event.ctrlKey ? 50 : 20;
    // 捕獲方向
    direct = event.keyCode;
    return false;
};
// 鍵盤松開清空速度和方向
document.onkeyup = function(event){
    direct = 0;
}

效果

優化思路

  • 定時器控制方向,鍵盤按下控制速度和捕獲方向,鍵盤松開清空速度和方向

這就好比一輛汽車,速度就像汽車的油門,定時器就像汽車的方向盤,而鍵盤就像汽車的離合和檔位

油門一直在踩著,發動機就一直勻速運轉,就能保證速度一直存在,啟動或轉向就不會出現卡頓的現象

當鍵盤按下時,就是鬆離合換擋位;當鍵盤松開時,就是踩離合

不過,跟現實世界不同的是,JS 的世界沒有慣性,所以只要鬆離合,div 就不會再移動了

2、延時呼叫

setTimeout()、clearTimeout()

延時呼叫,延時呼叫一個函式不馬上執行,而是隔一段時間以後在執行,而且只會執行一次

延時呼叫和定時呼叫的區別:定時呼叫會執行多次,而延時呼叫只會執行一次

延時呼叫和定時呼叫實際上是可以互相代替的,在開發中可以根據自己需要去選擇

var num = 1;
var timer = setInterval(function(){
 	console.log(num++); // 1 2 3 4 5 ...
}, 1000);
var timer = setTimeout(function(){
    console.log(num++); // 1
}, 1000);
clearTimeout(timer);

3、定時器的應用(一)

<練習:點選按鈕div移動>

HTML 程式碼

<button type="button" id="btn1">點選按鈕box1向右移動</button>
<button type="button" id="btn2">點選按鈕box1向左移動</button>
<br><br>
<div id="box1"></div>
<div id="line"></div>

CSS 程式碼

* {
    margin: 0;
    padding: 0;
}

#box1 {
    width: 100px;
    height: 100px;
    background-color: red;
    /* 開啟定位 */
    position: absolute;
    left: 0;
    top: 0;
}

#line {
    width: 0;
    height: 1000px;
    border: 1px solid black;
    position: absolute;
    top: 0;
    left: 800px;
    left: 0;
    top: 200px;
}

JS 程式碼

// 自定義相容所有瀏覽器獲取元素樣式的方法
function getStyle(obj, name) {
    return window.getComputedStyle ? getComputedStyle(obj, null)[name] : obj.currentStyle[name];
}
window.onload = function() {
    var timer;
    var speed = 19;
    var btn1 = document.getElementById("btn1");
    var btn2 = document.getElementById("btn2");
    var box1 = document.getElementById("box1");
    btn1.onclick = function() {
        // 清空上一個定時器
        clearInterval(timer);
        // 設定定時器
        timer = setInterval(function() {
            // 獲取舊值
            var oldValue = parseInt(getStyle(box1, "left"));
            // 獲取新值
            var newValue = oldValue + speed;
            // 當達到一定值時停下來
            newValue = newValue > 800 ? 800 : newValue;
            // 賦新值
            box1.style.left = newValue + "px";
            // 當值不再變化時,清空定時器
            if(newValue == 800){
                clearInterval(timer);
            }
        }, 50);
    };
    btn2.onclick = function() {
        // 清空上一個定時器
        clearInterval(timer);
        // 設定定時器
        timer = setInterval(function() {
            // 獲取舊值
            var oldValue = parseInt(getStyle(box1, "left"));
            // 獲取新值
            var newValue = oldValue - speed;
            // 當達到一定值時停下來
            newValue = newValue < 0 ? 0 : newValue;
            // 賦新值
            box1.style.left = newValue + "px";
            // 當值不再變化時,清空定時器
            if(newValue == 0){
                clearInterval(timer);
            }
        }, 50);
    };
}

優化1:封裝移動方法

// 封裝移動方法
// obj:要執行動畫的物件
// target:執行動畫的目標位置
// speed:移動的速度(正數向右移動,負數向左移動)
var timer;
function move(obj, target, speed) {
    clearInterval(timer);
    timer = setInterval(function() {
        var oldValue = parseInt(getStyle(obj, "left"));
        var newValue = oldValue + speed;
        newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target : newValue);
        obj.style.left = newValue + "px";
        if (newValue == target) {
            clearInterval(timer);
        }
    }, 50);
}

優化2:智慧判斷方向

function move(obj, target, speed) {
    clearInterval(timer);
    var current = parseInt(getStyle(obj, "left"));
    // 智慧判斷方向
    speed = target < current ? -speed : speed;
    timer = setInterval(function() {
        var oldValue = parseInt(getStyle(obj, "left"));
        var newValue = oldValue + speed;
        newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target : newValue);
        obj.style.left = newValue + "px";
        if (newValue == target) {
            clearInterval(timer);
        }
    }, 50);
}

優化3:消除多個div影響

目前我們的定時器的標識由全域性變數 timer 儲存,所有的執行正在執行的定時器都在這個變數中儲存

那麼我們就不能定義全域性的了,而是需要向執行動畫的物件中新增一個 timer 屬性,用來儲存它自己的定時器的標識

function move(obj, target, speed) {
    clearInterval(obj.timer);
    var current = parseInt(getStyle(obj, "left"));
    // 智慧判斷速度的方向
    speed = target < current ? -speed : speed;
    obj.timer = setInterval(function() {
        var oldValue = parseInt(getStyle(obj, "left"));
        var newValue = oldValue + speed;
        newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target :
                                                                          newValue);
        obj.style.left = newValue + "px";
        if (newValue == target) {
            clearInterval(obj.timer);
        }
    }, 50);
}

這樣,執行動畫的物件之間就不會再互相產生影響了

優化4:支援多屬性

只需要將left相關的屬性改為變數傳入

// obj:要執行動畫的物件
// attr:要執行動畫的樣式
// target:執行動畫的目標位置
// speed:移動的速度(正數向右移動,負數向左移動)
function move(obj, attr, target, speed) {
    clearInterval(obj.timer);
    var current = parseInt(getStyle(obj, attr));
    // 智慧判斷速度的方向
    speed = target < current ? -speed : speed;
    obj.timer = setInterval(function() {
        var oldValue = parseInt(getStyle(obj, attr));
        var newValue = oldValue + speed;
        newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target :
                                                                          newValue);
        obj.style[attr] = newValue + "px";
        if (newValue == target) {
            clearInterval(obj.timer);
        }
    }, 50);
}

呼叫修改後的函式

btn1.onclick = function() {
    move(box1, "left", 800, speed);
};
btn2.onclick = function() {
    move(box1, "left", 0, speed);
};
btn3.onclick = function() {
    move(box2, "top", 500, speed);
};
btn4.onclick = function() {
    move(box3, "height", 500, speed);
};

優化5:添加回調函式

// obj:要執行動畫的物件
// attr:要執行動畫的樣式
// target:執行動畫的目標位置
// speed:移動的速度(正數向右移動,負數向左移動)
// callback:回撥函式
function move(obj, attr, target, speed, callback) {
    clearInterval(obj.timer);
    var current = parseInt(getStyle(obj, attr));
    // 智慧判斷速度的方向
    speed = target < current ? -speed : speed;
    obj.timer = setInterval(function() {
        var oldValue = parseInt(getStyle(obj, attr));
        var newValue = oldValue + speed;
        newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target :
                                                                          newValue);
        obj.style[attr] = newValue + "px";
        if (newValue == target) {
            clearInterval(obj.timer);
            callback && callback(); // 即使不傳回調函式也不會報錯
        }
    }, 50);
}

呼叫回撥函式

btn4.onclick = function() {
    move(box3, "height", 500, speed, function(){
        move(box3, "width", 500, speed, function(){
            move(box3, "height", 100, speed, function(){
                move(box3, "width", 100, speed, function(){

                });
            });
        });
    });
};

優化6:封裝JS檔案

新建 js 資料夾,新建 tools.js 檔案,複製 move 相關方法

// 自定義相容所有瀏覽器獲取元素樣式的方法
function getStyle(obj, name) {
	return window.getComputedStyle ? getComputedStyle(obj, null)[name] : obj.currentStyle[name];
}
// 封裝移動方法
// obj:要執行動畫的物件
// attr:要執行動畫的樣式
// target:執行動畫的目標位置
// speed:移動的速度
// callback:回撥函式
function move(obj, attr, target, speed, callback) {
	clearInterval(obj.timer);
	var current = parseInt(getStyle(obj, attr));
	speed = target < current ? -speed : speed;
	obj.timer = setInterval(function() {
		var oldValue = parseInt(getStyle(obj, attr));
		var newValue = oldValue + speed;
		newValue = speed > 0 ? (newValue > target ? target : newValue) : (newValue < target ? target :
			newValue);
		obj.style[attr] = newValue + "px";
		if (newValue == target) {
			clearInterval(obj.timer);
			callback && callback();
		}
	}, 50);
}

最後再引入 js 檔案,大功告成

<script src="js/tools.js" type="text/javascript" charset="utf-8"></script>

4、定時器應用(二)

<練習:輪播圖>

HTML 程式碼

<div id="outer">
    <ul id="pic-list">
        <li><img src="img/1.jpg" /></li>
        <li><img src="img/2.jpg" /></li>
        <li><img src="img/3.jpg" /></li>
        <li><img src="img/4.jpg" /></li>
        <li><img src="img/5.jpg" /></li>
        <li><img src="img/1.jpg" /></li>
    </ul>
    <ul id="nav-list">
        <li><a href="javascript:;"></a></li>
        <li><a href="javascript:;"></a></li>
        <li><a href="javascript:;"></a></li>
        <li><a href="javascript:;"></a></li>
        <li><a href="javascript:;"></a></li>
    </ul>
</div>

CSS 程式碼

/* 去除瀏覽器預設樣式 */
* {
	margin: 0;
	padding: 0;
}

ul {
	list-style: none;
}

a{
	text-decoration: none;
}

/* 總體佈局 */
#outer {
	width: 500px;
	height: 332px;
	margin: 100px auto;
	box-shadow: 10px 10px 5px rgba(0, 0, 0, .2);
	position: relative;
	overflow: hidden;
}

/* 輪播圖片 */
#pic-list {
	width: 2550px;
	position: absolute;
	left: 0;
	top: 0;
	/* 新增過渡效果 */
	/* transition: left 0.3s; */
}

#pic-list li {
	float: left;
	margin-right: 10px;
}

/* 輪播按鈕 */
#nav-list {
	position: absolute;
	left: 187.5px;
	bottom: 10px;
}

#nav-list li{
	float: left;
	width: 15px;
	height: 15px;
	background-color: red;
	opacity: 0.5;
	margin: 0 5px;
}

#nav-list a:hover{
	background-color: black;
}

#nav-list li a{
	display: block;
	height: 100%;
	line-height: 100%;
}

JS 程式碼

window.onload = function() {
    var outer = document.getElementById("outer");
    var picList = document.getElementById("pic-list");
    var imgArr = document.getElementsByTagName("img");
    var navList = document.getElementById("nav-list");
    var aArr = document.getElementsByTagName("a");
    var index = 0;
    
    // 動態調整picList的寬度,以自適應圖片的數量變化
    picList.style.width = (outer.clientWidth + 10) * imgArr.length + "px";
    // 動態調整navList的水平偏移量
    navList.style.left = (outer.clientWidth - 25 * aArr.length) / 2 + "px";
    // 設定第一個輪播按鈕懸浮顏色
    aArr[index].style.backgroundColor = "black";

    // 點選按鈕切換圖片
    for (var i = 0; i < aArr.length; i++) {
        aArr[i].index = i;
        aArr[i].onclick = function() {
            // 設定index
            index = this.index;
            // 清空定時器
            clearInterval(timer);
            move(picList, "left", -(outer.clientWidth + 10) * index, 100, function() {
                // 開啟定時器
                autoSwitch();
            });
            setColor();
        };
    }

    autoSwitch();

    // 自動切換圖片
    var timer;
    function autoSwitch() {
        timer = setInterval(function() {
            index++;
            index %= imgArr.length;
            move(picList, "left", -(outer.clientWidth + 10) * index, 100, function() {
                if (index >= imgArr.length - 1) {
                    picList.style.left = "0px";
                }
                setColor();
            });
        }, 3000);
    }

    // 設定輪播按鈕懸浮顏色
    function setColor() {
        // 重置所有輪播按鈕顏色:由於修改的是內聯樣式,優先順序較高,會把css樣式覆蓋,導致懸浮效果失效
        // 那麼這裡不使用內聯樣式,將其置為空,這樣就會找css樣式
        for (var i = 0; i < aArr.length; i++) {
            aArr[i].style.backgroundColor = "";
        }
        index %= aArr.length;
        aArr[index].style.backgroundColor = "black";
    }
};

5、類的操作

修改class屬性

HTML 程式碼

<button type="button" id="btn1">點選按鈕修改box1樣式</button>
<br><br>
<div id="box1" class="b1"></div>

CSS 程式碼

.b1{
    width: 100px;
    height: 100px;
    background-color: red;
}

JS 程式碼

box1.style.width = "200px";
box1.style.height = "200px";
box1.style.backgroundColor = "yellow";

通過style屬性來修改元素的樣式,每修改一個樣式,瀏覽器就需要重新渲染一次頁面

這樣執行的效能是比較差的,而且這種形式當我們要修改多個樣式時,也不太方便

那怎麼辦呢?

我們可以先事先定義好一個 class 屬性,裡面寫好我們需要變化的樣式

.b2{
    width: 200px;
    height: 200px;
    background-color: yellow;
}

然後在 JS 中修改className屬性即可

box1.className = "b2";

效果是一樣的

我們可以通過修改元素的class屬性來間接的修改樣式

這樣一來,我們只需要修改一次,即可同時修改多個樣式

瀏覽器只需要重新渲染頁面一次,效能比較好,並且這種方式,可以使表現和行為進一步的分離

新增class屬性

我們可以在此樣式基礎之上,定義一個函式,用來向一個元素中新增指定的 class 屬性值

// 引數:
// obj 要新增class屬性的元素
// cn  要新增的class值
function addClass(obj, cn){
    obj.className += " " + cn;
}
//.b3{
//    position: absolute;
//    left: 100px;
//}
addClass(box1, "b3");

但是也存在一個問題,雖然從效果上來看沒有什麼不同,但多次點選後會重複新增相同的 class 屬性,而這個操作是多餘的

我們就需要在寫一個函式來判斷是否已經存在 class 屬性

function hasClass(obj, cn) {
    // return obj.className.indexOf(cn) != -1;
    var reg = new RegExp("\\b"+cn+"\\b");
    return reg.test(obj.className);
}
function addClass(obj, cn) {
    if (!hasClass(obj, cn)) {
        obj.className += " " + cn;
    }
}

刪除class屬性

刪除一個元素中的指定的 class 屬性

function removeClass(obj, cn) {
    var reg = new RegExp("\\b" + cn + "\\b");
    obj.className = obj.className.replace(reg, "");
}

切換class屬性

// toggleClass可以用來切換一個類
// 如果元素中具有該類,則刪除
// 如果元素中沒有該類,則新增
function toggleClass(obj, cn) {
    if (hasClass(obj, cn)) {
        removeClass(obj, cn);
    } else {
        addClass(obj, cn);
    }
}

<練習:二級選單>

HTML 程式碼

<div id="my_menu" class="sdmenu">
    <div>
        <span class="menuSpan">線上工具</span>
        <a href="#">影象優化</a>
        <a href="#">收藏夾圖示生成器</a>
        <a href="#">郵件</a>
        <a href="#">htaccess密碼</a>
        <a href="#">梯度影象</a>
        <a href="#">按鈕生成器</a>
    </div>
    <div class="collapsed">
        <span class="menuSpan">支援我們</span>
        <a href="#">推薦我們</a>
        <a href="#">連結我們</a>
        <a href="#">網路資源</a>
    </div>
    <div class="collapsed">
        <span class="menuSpan">合作伙伴</span>
        <a href="#">JavaScript工具包</a>
        <a href="#">CSS驅動</a>
        <a href="#">CodingForums</a>
        <a href="#">CSS例子</a>
    </div>
    <div class="collapsed">
        <span class="menuSpan">測試電流</span>
        <a href="#">Current or not</a>
        <a href="#">Current or not</a>
        <a href="#">Current or not</a>
        <a href="#">Current or not</a>
    </div>
</div>

CSS 程式碼

@charset "utf-8";

/* sdmenu */

div.sdmenu {
	width: 150px;
	margin: 0 auto;
	font-family: Arial, sans-serif;
	font-size: 12px;
	padding-bottom: 10px;
	background: url(bottom.gif) no-repeat right bottom;
	color: #fff;
}

div.sdmenu div {
	background: url(title.gif) repeat-x;
	overflow: hidden;
}

div.sdmenu div:first-child {
	background: url(toptitle.gif) no-repeat;
}

div.sdmenu div.collapsed {
	height: 25px;
}

div.sdmenu div span {
	display: block;
	height: 15px;
	line-height: 15px;
	overflow: hidden;
	padding: 5px 25px;
	font-weight: bold;
	color: white;
	background: url(expanded.gif) no-repeat 10px center;
	cursor: pointer;
	border-bottom: 1px solid #ddd;
}

div.sdmenu div.collapsed span {
	background-image: url(collapsed.gif);
}

div.sdmenu div a {
	padding: 5px 10px;
	background: #eee;
	display: block;
	border-bottom: 1px solid #ddd;
	color: #066;
}

div.sdmenu div a.current {
	background: #ccc;
}

div.sdmenu div a:hover {
	background: #066 url(linkarrow.gif) no-repeat right center;
	color: #fff;
	text-decoration: none;
}

JS 程式碼

// 為相容IE8,用querySelectorAll
var menuSpan = document.querySelectorAll(".menuSpan");
var myMenu = document.getElementById("my_menu");
var thisNode;
// 定義一個變數,來儲存當前開啟的選單
var openNode = menuSpan[0].parentNode;
for (var i = 0; i < menuSpan.length; i++) {
    // 一級選單繫結單擊響應函式
    menuSpan[i].onclick = function() {
        thisNode = this.parentNode;
        // 切換collapsed的class屬性
        toggleClass(thisNode, "collapsed");
        // 開啟選單以後,應該關閉之前開啟的選單
        if (openNode != thisNode && !hasClass(openNode, "collapsed")) {
            // 為了可以統一處理動畫過渡效果,我們希望在這將addClass改為toggleClass
            // addClass(openNode, "collapsed");
            // 此處toggleClass()不需要有移除的功能
            toggleClass(openNode, "collapsed");
        }
        openNode = thisNode;
    };
}

新增動畫的過渡效果

var beginHeight;
var endHeight;
for (var i = 0; i < menuSpan.length; i++) {
    menuSpan[i].onclick = function() {
        thisNode = this.parentNode;
        // 切換前高度
        beginHeight = thisNode.offsetHeight;
        // 切換
        toggleClass(thisNode, "collapsed");
        // 切換後高度
        endHeight = thisNode.offsetHeight;
        // 動畫執行前內聯高度
        thisNode.style.height = beginHeight + "px";
        // 設定動畫效果
        move(thisNode, "height", endHeight, 30, function(){

        });
        if (openNode != thisNode && !hasClass(openNode, "collapsed")) {
            toggleClass(openNode, "collapsed");
        }
        openNode = thisNode;
    };
}

因為我們執行動畫前添加了一個內聯高度,而內聯屬性的優先順序是最高的

當新增collapsed的 class 屬性後不會起作用,因此同時需要在動畫執行完畢後去除內聯樣式

move(thisNode, "height", endHeight, 30, function(){
    // 動畫執行後內聯高度
    thisNode.style.height = "";
});

我們只對展開添加了動畫效果,摺疊時並沒有新增動畫

因為新增動畫的邏輯是一致的,所以這裡我們可以封裝一個函式,用來執行帶有動畫效果的摺疊和展開動作

// 帶有動畫效果的摺疊和展開動作
function toggleMenu(obj) {
    // 切換前高度
    beginHeight = obj.offsetHeight;
    // 切換
    toggleClass(obj, "collapsed");
    // 切換後高度
    endHeight = obj.offsetHeight;
    // 動畫執行前內聯高度
    obj.style.height = beginHeight + "px";
    // 設定動畫效果
    move(obj, "height", endHeight, 30, function() {
        // 動畫執行後內聯高度
        obj.style.height = "";
    });
}

呼叫 toggleMenu 函式

for (var i = 0; i < menuSpan.length; i++) {
    menuSpan[i].onclick = function() {
        thisNode = this.parentNode;
        // 切換
        toggleMenu(thisNode);
        // 關閉其他div
        if (openNode != thisNode && !hasClass(openNode, "collapsed")) {
            toggleMenu(openNode);
        }
        openNode = thisNode;
    };
}