@@ -35,7 +35,7 @@ function RemoteFunctions(config = {}) {
3535
3636 // this will store the element that was clicked previously (before the new click)
3737 // we need this so that we can remove click styling from the previous element when a new element is clicked
38- let previouslyClickedElement = null ;
38+ let previouslySelectedElement = null ;
3939
4040 var req , timeout ;
4141 function animateHighlight ( time ) {
@@ -709,7 +709,7 @@ function RemoteFunctions(config = {}) {
709709 _clickHighlight . clear ( ) ;
710710 _clickHighlight . add ( element , true ) ;
711711
712- previouslyClickedElement = element ;
712+ previouslySelectedElement = element ;
713713 }
714714
715715 function disableHoverListeners ( ) {
@@ -835,37 +835,80 @@ function RemoteFunctions(config = {}) {
835835 }
836836 }
837837
838- // highlight a rule
838+ /**
839+ * Find the best element to select from a list of matched nodes
840+ * Prefers: previously selected element > parent of selected > first valid element
841+ * @param {NodeList } nodes - The nodes matching the CSS rule
842+ * @param {string } rule - The CSS rule used to match nodes
843+ * @returns {{element: Element|null, skipSelection: boolean} } - The element to select and whether to skip selection
844+ */
845+ function findBestElementToSelect ( nodes , rule ) {
846+ let firstValidElement = null ;
847+ let elementToSelect = null ;
848+
849+ for ( let i = 0 ; i < nodes . length ; i ++ ) {
850+ if ( ! LivePreviewView . isElementInspectable ( nodes [ i ] , true ) || nodes [ i ] . tagName === "BR" ) {
851+ continue ;
852+ }
853+
854+ // Store the first valid element as a fallback
855+ if ( ! firstValidElement ) {
856+ firstValidElement = nodes [ i ] ;
857+ }
858+
859+ // if hover lock timer is active, skip selection as it's already handled by handleElementClick
860+ if ( _hoverLockTimer && nodes [ i ] === previouslySelectedElement ) {
861+ return { element : null , skipSelection : true } ;
862+ }
863+
864+ // Check if the currently selected element or any of its parents have a highlight
865+ if ( previouslySelectedElement ) {
866+ if ( nodes [ i ] === previouslySelectedElement ) {
867+ // Exact match - prefer this
868+ elementToSelect = previouslySelectedElement ;
869+ break ;
870+ } else if ( ! elementToSelect &&
871+ previouslySelectedElement . closest && nodes [ i ] === previouslySelectedElement . closest ( rule ) ) {
872+ // The node is a parent of the currently selected element. we stop at the first parent, after that
873+ // we only scan for exact match
874+ elementToSelect = nodes [ i ] ;
875+ }
876+ }
877+ }
878+
879+ return {
880+ element : elementToSelect || firstValidElement ,
881+ skipSelection : false
882+ } ;
883+ }
884+
885+ /**
886+ * Highlight all elements matching a CSS rule and select the best one
887+ * @param {string } rule - The CSS rule to highlight
888+ */
839889 function highlightRule ( rule ) {
840890 hideHighlight ( ) ;
841- var i , nodes = window . document . querySelectorAll ( rule ) ;
891+ const nodes = window . document . querySelectorAll ( rule ) ;
842892
843- for ( i = 0 ; i < nodes . length ; i ++ ) {
893+ // Highlight all matching nodes
894+ for ( let i = 0 ; i < nodes . length ; i ++ ) {
844895 highlight ( nodes [ i ] ) ;
845896 }
897+
846898 if ( _clickHighlight ) {
847899 _clickHighlight . selector = rule ;
848900 }
849901
850- // select the first valid highlighted element
851- let foundValidElement = false ;
852- for ( i = 0 ; i < nodes . length ; i ++ ) {
853- if ( LivePreviewView . isElementInspectable ( nodes [ i ] , true ) && nodes [ i ] . tagName !== "BR" ) {
854- // if hover lock timer is active, we don't call selectElement as,
855- // it means that its already called by handleElementClick function
856- if ( _hoverLockTimer && nodes [ i ] === previouslyClickedElement ) {
857- foundValidElement = true ;
858- break ;
859- }
860- selectElement ( nodes [ i ] ) ;
861- foundValidElement = true ;
862- break ;
863- }
864- }
902+ // Find and select the best element
903+ const { element, skipSelection } = findBestElementToSelect ( nodes , rule ) ;
865904
866- // if no valid element present we dismiss the boxes
867- if ( ! foundValidElement ) {
868- dismissUIAndCleanupState ( ) ;
905+ if ( ! skipSelection ) {
906+ if ( element ) {
907+ selectElement ( element ) ;
908+ } else {
909+ // No valid element found, dismiss UI
910+ dismissUIAndCleanupState ( ) ;
911+ }
869912 }
870913
871914 // In edit mode, create temporary highlights AFTER selection to avoid clearing
@@ -1195,7 +1238,7 @@ function RemoteFunctions(config = {}) {
11951238 this . rememberedNodes = { } ;
11961239
11971240 // this check makes sure that if the element is no more in the DOM then we remove it
1198- if ( previouslyClickedElement && ! previouslyClickedElement . isConnected ) {
1241+ if ( previouslySelectedElement && ! previouslySelectedElement . isConnected ) {
11991242 dismissUIAndCleanupState ( ) ;
12001243 } else {
12011244 redrawEverything ( ) ;
@@ -1250,14 +1293,14 @@ function RemoteFunctions(config = {}) {
12501293 * Helper function to cleanup previously clicked element highlighting and state
12511294 */
12521295 function cleanupPreviousElementState ( ) {
1253- if ( previouslyClickedElement ) {
1254- if ( previouslyClickedElement . _originalOutline !== undefined ) {
1255- previouslyClickedElement . style . outline = previouslyClickedElement . _originalOutline ;
1296+ if ( previouslySelectedElement ) {
1297+ if ( previouslySelectedElement . _originalOutline !== undefined ) {
1298+ previouslySelectedElement . style . outline = previouslySelectedElement . _originalOutline ;
12561299 } else {
1257- previouslyClickedElement . style . outline = "" ;
1300+ previouslySelectedElement . style . outline = "" ;
12581301 }
1259- delete previouslyClickedElement . _originalOutline ;
1260- previouslyClickedElement = null ;
1302+ delete previouslySelectedElement . _originalOutline ;
1303+ previouslySelectedElement = null ;
12611304 }
12621305
12631306 if ( config . mode === 'edit' ) {
0 commit comments