Skip to content

Commit 60a2f66

Browse files
committed
DOM操作の最適化
- 大量要素の一括生成をバッチ処理(5-10要素ずつ)に変更 - 各バッチ間でブラウザに処理時間を与える
1 parent 5dd2bd0 commit 60a2f66

File tree

1 file changed

+94
-45
lines changed

1 file changed

+94
-45
lines changed

js/kunai/ui/treeview.js

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,39 @@ class DOM {
5454
if (h.classes && h.classes.length) {
5555
empty = false
5656
let classes = $('<ul>', {class: 'classes'}).appendTo(elem)
57-
classes.append(await Promise.all(h.classes.map(async (c) => {
58-
return await this.makeClass(c)
59-
})))
57+
// バッチ処理で段階的に要素を追加
58+
const classBatchSize = 10
59+
for (let i = 0; i < h.classes.length; i += classBatchSize) {
60+
const batch = h.classes.slice(i, i + classBatchSize)
61+
const batchElements = await Promise.all(batch.map(async (c) => {
62+
return await this.makeClass(c)
63+
}))
64+
classes.append(batchElements)
65+
66+
// iOS Safariに処理時間を与える
67+
if (i + classBatchSize < h.classes.length) {
68+
await new Promise(resolve => setTimeout(resolve, 0))
69+
}
70+
}
6071
}
6172

6273
if (h.others && h.others.length) {
6374
empty = false
6475
let others = $('<ul>', {class: 'others'}).appendTo(elem)
65-
others.append(await Promise.all(h.others.map(async (o) => {
66-
return await this.makeOther(o)
67-
})))
76+
// バッチ処理で段階的に要素を追加
77+
const otherBatchSize = 10
78+
for (let i = 0; i < h.others.length; i += otherBatchSize) {
79+
const batch = h.others.slice(i, i + otherBatchSize)
80+
const batchElements = await Promise.all(batch.map(async (o) => {
81+
return await this.makeOther(o)
82+
}))
83+
others.append(batchElements)
84+
85+
// iOS Safariに処理時間を与える
86+
if (i + otherBatchSize < h.others.length) {
87+
await new Promise(resolve => setTimeout(resolve, 0))
88+
}
89+
}
6890
}
6991

7092
if (empty) {
@@ -359,48 +381,63 @@ class Treeview {
359381

360382
const cats = this.kc.categories()
361383

362-
root.append(await Promise.all(
363-
this.tree.filter((top) => top.category.index !== cats.get('index').index).map(async (top) => {
364-
const topID = top.namespace.namespace[0]
365-
let stack = $('<li>', {
366-
class: 'top stack',
367-
'data-top-id': topID,
368-
})
369-
370-
this.dom.topElems.set(topID, stack)
371-
372-
stack.append(
373-
$('<div>', {class: 'heading'})
374-
.append(
375-
$('<div>', {class: 'expander'}).on(
376-
'click', async () => { this.dom.doStackExpand(topID) }
384+
// バッチ処理で段階的にDOM要素を生成
385+
const filteredTops = this.tree.filter((top) => top.category.index !== cats.get('index').index)
386+
const batchSize = 5 // 一度に処理する要素数
387+
388+
for (let i = 0; i < filteredTops.length; i += batchSize) {
389+
const batch = filteredTops.slice(i, i + batchSize)
390+
const batchElements = await Promise.all(
391+
batch.map(async (top) => {
392+
const topID = top.namespace.namespace[0]
393+
let stack = $('<li>', {
394+
class: 'top stack',
395+
'data-top-id': topID,
396+
})
397+
398+
this.dom.topElems.set(topID, stack)
399+
400+
stack.append(
401+
$('<div>', {class: 'heading'})
402+
.append(
403+
$('<div>', {class: 'expander'}).on(
404+
'click', async () => { this.dom.doStackExpand(topID) }
405+
)
377406
)
378-
)
379-
.append(await this.dom.makeTitle(top))
380-
)
407+
.append(await this.dom.makeTitle(top))
408+
)
381409

382-
let content_wrapper =
383-
$('<div>', {class: 'content-wrapper'})
384-
.appendTo(stack)
410+
let content_wrapper =
411+
$('<div>', {class: 'content-wrapper'})
412+
.appendTo(stack)
385413

386-
let content =
387-
$('<div>', {class: 'content'})
388-
.appendTo(content_wrapper)
414+
let content =
415+
$('<div>', {class: 'content'})
416+
.appendTo(content_wrapper)
389417

390-
let is_not_empty = false
391-
if (top.category.index === cats.get('lang').index) {
392-
is_not_empty = await this.processLangTop(top, content)
393-
} else {
394-
is_not_empty = await this.processTop(top, content)
395-
}
418+
let is_not_empty = false
419+
if (top.category.index === cats.get('lang').index) {
420+
is_not_empty = await this.processLangTop(top, content)
421+
} else {
422+
is_not_empty = await this.processTop(top, content)
423+
}
396424

397-
if (!is_not_empty) {
398-
stack.addClass('empty')
399-
}
425+
if (!is_not_empty) {
426+
stack.addClass('empty')
427+
}
400428

401-
return stack
402-
})
403-
))
429+
return stack
430+
})
431+
)
432+
433+
// バッチごとにDOMに追加
434+
root.append(batchElements)
435+
436+
// iOS Safariに処理時間を与える
437+
if (i + batchSize < filteredTops.length) {
438+
await new Promise(resolve => setTimeout(resolve, 0))
439+
}
440+
}
404441
}
405442

406443
async processTop(top, e) {
@@ -409,9 +446,21 @@ class Treeview {
409446
if (top.articles && top.articles.length) {
410447
is_empty = false
411448

412-
let self = $('<ul>', {class: 'articles'}).append(await Promise.all(top.articles.map(async (ar) => {
413-
return await this.dom.makeArticle(ar)
414-
})))
449+
let self = $('<ul>', {class: 'articles'})
450+
// バッチ処理で段階的に要素を追加
451+
const articleBatchSize = 10
452+
for (let i = 0; i < top.articles.length; i += articleBatchSize) {
453+
const batch = top.articles.slice(i, i + articleBatchSize)
454+
const batchElements = await Promise.all(batch.map(async (ar) => {
455+
return await this.dom.makeArticle(ar)
456+
}))
457+
self.append(batchElements)
458+
459+
// iOS Safariに処理時間を与える
460+
if (i + articleBatchSize < top.articles.length) {
461+
await new Promise(resolve => setTimeout(resolve, 0))
462+
}
463+
}
415464

416465
e.append(await this.dom.kunaiBranch(self, 'articles'))
417466
}

0 commit comments

Comments
 (0)