@@ -227,6 +227,21 @@ const _blockMakeBitmap = (data, callback, args) => {
227227 img . src = "data:image/svg+xml;base64," + window . btoa ( base64Encode ( data ) ) ;
228228} ;
229229
230+ // Optimization: Cache static DOM elements to avoid repetitive querySelector/getElementById calls
231+ const _domCache = {
232+ wheelDiv : null ,
233+ contextWheelDiv : null ,
234+ helpfulWheelDiv : null ,
235+ labelDiv : null
236+ } ;
237+
238+ const _getStatic = id => {
239+ if ( ! _domCache [ id ] ) {
240+ _domCache [ id ] = docById ( id ) ;
241+ }
242+ return _domCache [ id ] ;
243+ } ;
244+
230245/**
231246 * Define block instance objects and any methods that are intra-block.
232247 * Represents a block instance with associated methods.
@@ -2785,7 +2800,7 @@ class Block {
27852800 this . _calculateBlockHitArea ( ) ;
27862801
27872802 this . container . on ( "mouseover" , ( ) => {
2788- docById ( "contextWheelDiv" ) . style . display = "none" ;
2803+ _getStatic ( "contextWheelDiv" ) . style . display = "none" ;
27892804
27902805 if ( ! that . activity . logo . runningLilypond ) {
27912806 document . body . style . cursor = "pointer" ;
@@ -2807,14 +2822,17 @@ class Block {
28072822 * @param {Event } event - The click event.
28082823 */
28092824 this . container . on ( "click" , event => {
2810- if ( docById ( "helpfulWheelDiv" ) && docById ( "helpfulWheelDiv" ) . style . display !== "none" ) {
2811- docById ( "helpfulWheelDiv" ) . style . display = "none" ;
2825+ if (
2826+ _getStatic ( "helpfulWheelDiv" ) &&
2827+ _getStatic ( "helpfulWheelDiv" ) . style . display !== "none"
2828+ ) {
2829+ _getStatic ( "helpfulWheelDiv" ) . style . display = "none" ;
28122830 }
28132831 // We might be able to check which button was clicked.
28142832 if ( "nativeEvent" in event ) {
28152833 if ( "button" in event . nativeEvent && event . nativeEvent . button == 2 ) {
28162834 that . blocks . stageClick = true ;
2817- docById ( "wheelDiv" ) . style . display = "none" ;
2835+ _getStatic ( "wheelDiv" ) . style . display = "none" ;
28182836 that . blocks . activeBlock = thisBlock ;
28192837 piemenuBlockContext ( that ) ;
28202838 return ;
@@ -3427,7 +3445,7 @@ class Block {
34273445 labelValue = this . value ;
34283446 }
34293447
3430- const labelElem = docById ( "labelDiv" ) ;
3448+ const labelElem = _getStatic ( "labelDiv" ) ;
34313449
34323450 const safetext = text => {
34333451 // Best to avoid using these special characters in text strings
@@ -3447,12 +3465,29 @@ class Block {
34473465 } ;
34483466
34493467 if ( this . name === "text" ) {
3450- labelElem . innerHTML =
3451- '<input id="textLabel" style="position: absolute; -webkit-user-select: text;-moz-user-select: text;-ms-user-select: text;" class="text" type="text" value="' +
3452- safetext ( labelValue ) +
3453- '" />' ;
3468+ // Reuse existing input element or create a new one
3469+ if ( ! _domCache . textLabelInput ) {
3470+ const el = document . createElement ( "input" ) ;
3471+ el . id = "textLabel" ;
3472+ el . style . position = "absolute" ;
3473+ el . style . webkitUserSelect = "text" ;
3474+ el . style . mozUserSelect = "text" ;
3475+ el . style . msUserSelect = "text" ;
3476+ el . className = "text" ;
3477+ el . type = "text" ;
3478+ _domCache . textLabelInput = el ;
3479+ }
3480+
3481+ // Ensure it is the child of labelElem
3482+ if ( _domCache . textLabelInput . parentNode !== labelElem ) {
3483+ labelElem . innerHTML = "" ;
3484+ labelElem . appendChild ( _domCache . textLabelInput ) ;
3485+ }
3486+
3487+ this . label = _domCache . textLabelInput ;
3488+ this . label . value = safetext ( labelValue ) ;
3489+ this . label . style . display = "" ;
34543490 labelElem . classList . add ( "hasKeyboard" ) ;
3455- this . label = docById ( "textLabel" ) ;
34563491
34573492 // set the position of cursor to the end (for text value)
34583493 const valueLength = this . label . value . length ;
@@ -3993,12 +4028,29 @@ class Block {
39934028 break ;
39944029 }
39954030 } else {
3996- labelElem . innerHTML =
3997- '<input id="numberLabel" style="position: absolute; -webkit-user-select: text;-moz-user-select: text;-ms-user-select: text;" class="number" type="number" value="' +
3998- labelValue +
3999- '" />' ;
4031+ // Reuse existing input element or create a new one
4032+ if ( ! _domCache . numberLabelInput ) {
4033+ const el = document . createElement ( "input" ) ;
4034+ el . id = "numberLabel" ;
4035+ el . style . position = "absolute" ;
4036+ el . style . webkitUserSelect = "text" ;
4037+ el . style . mozUserSelect = "text" ;
4038+ el . style . msUserSelect = "text" ;
4039+ el . className = "number" ;
4040+ el . type = "number" ;
4041+ el . step = "any" ;
4042+ _domCache . numberLabelInput = el ;
4043+ }
4044+
4045+ // Ensure it is the child of labelElem
4046+ if ( _domCache . numberLabelInput . parentNode !== labelElem ) {
4047+ labelElem . innerHTML = "" ;
4048+ labelElem . appendChild ( _domCache . numberLabelInput ) ;
4049+ }
4050+
4051+ this . label = _domCache . numberLabelInput ;
4052+ this . label . value = safetext ( labelValue ) ;
40004053 labelElem . classList . add ( "hasKeyboard" ) ;
4001- this . label = docById ( "numberLabel" ) ;
40024054
40034055 // set the position of cursor to the end (for number value)
40044056 const valueLength = this . label . value . length ;
@@ -4075,16 +4127,18 @@ class Block {
40754127 that . _labelChanged ( false , true ) ;
40764128 } ) ;
40774129
4078- this . label . style . left =
4079- Math . round (
4080- ( x + this . activity . blocksContainer . x ) * this . activity . getStageScale ( ) +
4081- canvasLeft
4082- ) + "px" ;
4083- this . label . style . top =
4084- Math . round (
4085- ( y + this . activity . blocksContainer . y ) * this . activity . getStageScale ( ) +
4086- canvasTop
4087- ) + "px" ;
4130+ // Use GPU acceleration (transform) instead of left/top to avoid layout thrashing
4131+ const left = Math . round (
4132+ ( x + this . activity . blocksContainer . x ) * this . activity . getStageScale ( ) + canvasLeft
4133+ ) ;
4134+ const top = Math . round (
4135+ ( y + this . activity . blocksContainer . y ) * this . activity . getStageScale ( ) + canvasTop
4136+ ) ;
4137+
4138+ this . label . style . transform = `translate3d(${ left } px, ${ top } px, 0)` ;
4139+ this . label . style . left = "0px" ;
4140+ this . label . style . top = "0px" ;
4141+
40884142 this . label . style . width =
40894143 Math . round ( ( selectorWidth * this . blocks . blockScale * this . protoblock . scale ) / 2 ) +
40904144 "px" ;
0 commit comments