brackets on both sides

add brackets at both sides of highlighted text when select some text and typing brackets. just like some texteditor

当前为 2024-05-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name brackets on both sides
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-05-17
  5. // @description add brackets at both sides of highlighted text when select some text and typing brackets. just like some texteditor
  6. // @author linche0502
  7. // @match *://*/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Your code here...
  16. // 選取文字後, 輸入括號改為在選取範圍左右新增括號
  17. function autoBrackets(targets){
  18. // targets是query selector text的話就直接搜尋, 尋找元素
  19. if(typeof(targets) == "string"){
  20. targets= document.querySelectorAll(targets);
  21. // 如果targets是單一元素的話, 就加到一個新的array裡, 以便使用forEach(我就懶)
  22. }else if(Node.prototype.isPrototypeOf(targets)){
  23. targets=[targets];
  24. }
  25. targets.forEach(target => {
  26. target.addEventListener("keydown", (event) => {
  27. const BRACKETS= {
  28. "Digit9": {true:"()"},
  29. "BracketLeft": {true:"{}", false:"[]"},
  30. "Quote": {true:'""', false:"''"},
  31. "Comma": {true:"<>"}
  32. };
  33. // 確認新輸入的內容是括號
  34. // if(!Object.keys(BRACKETS).includes(event.data)) // input event無法取消預設行為
  35. if(!(BRACKETS[event.code] && BRACKETS[event.code][event.shiftKey])){
  36. return
  37. }
  38. // 判斷反白區域是在(input/textarea)或是(contentEditable="false")的元素
  39. const focusElement= document.activeElement;
  40. // 反白區域是在(input/textarea)時
  41. if(["INPUT","TEXTAREA"].includes(focusElement.tagName)){
  42. const offset= [focusElement.selectionStart, focusElement.selectionEnd];
  43. // 先尋找是否有反白的區域
  44. if(offset[0] == offset[1]){
  45. return;
  46. }
  47. focusElement.value= focusElement.value.slice(0,offset[0])+
  48. BRACKETS[event.code][event.shiftKey][0]+
  49. focusElement.value.slice(offset[0],offset[1])+
  50. BRACKETS[event.code][event.shiftKey][1]+
  51. focusElement.value.slice(offset[1]);
  52. // 在更改文字內容後, 反白範圍會自動取消選取, 重新選取
  53. focusElement.setSelectionRange(offset[0]+1, offset[1]+1);
  54. }
  55. // 反白區域是在(contentEditable="false")的元素時
  56. else{
  57. const selection = window.getSelection()
  58. let selectNodes= [selection.anchorNode, selection.focusNode];
  59. let offset= [selection.anchorOffset, selection.focusOffset];
  60. // 先尋找是否有反白的區域
  61. if(selectNodes[0]===selectNodes[1] && offset[0]===offset[1]){
  62. return;
  63. }
  64. // 確認開始和結束的點沒有超出target的範圍
  65. if (!target.contains(selectNodes[0]) || !target.contains(selectNodes[1])) {
  66. return
  67. }
  68. // 確認anchorNode和focusNode在document中的順序, 以避免從後向前反白時會出錯
  69. if((selectNodes[0]===selectNodes[1] && offset[1]<offset[0]) || (selectNodes[0].compareDocumentPosition(selectNodes[1]) & Node.DOCUMENT_POSITION_PRECEDING)){
  70. selectNodes= selectNodes.reverse()
  71. offset= offset.reverse()
  72. }
  73. // 尋找兩者所在的共同父元素
  74. let checkNode= selectNodes[0];
  75. while(true){
  76. // 如果現在的checkNode不包含(或不等於)focusNode, 則再向上尋找, 最後checkNode即為anchorNode與focus的共同父元素
  77. if(!checkNode.contains(selectNodes[1])){
  78. checkNode= checkNode.parentNode;
  79. continue;
  80. }
  81. break;
  82. }
  83. // 如果共同父元素並不是一個可供編輯或輸入的元素, 或者不在一個可供編輯或輸入的元素之內, 則不進行動作
  84. while(true){
  85. if(checkNode.contentEditable == "true"){
  86. break
  87. }
  88. // 多加這一次判斷, 避免在contentEditable=="true"的元素裡面又有contentEditable=="false"的元素時會誤觸
  89. if(checkNode.contentEditable == "false"){
  90. return
  91. }
  92. // 向上尋找是否為可供編輯或輸入的元素時, 找到target為止, 不再向上尋找
  93. if(target.contains(checkNode) && !target.isEqualNode(checkNode)){
  94. checkNode= checkNode.parentNode;
  95. continue;
  96. }
  97. return;
  98. }
  99. // 在前後插入相對應的括號, 前括號和後括號分兩次個別加入, 以避免分別處在不同元素中時會發生錯誤
  100. selectNodes[0].textContent= selectNodes[0].textContent.slice(0,offset[0])+ BRACKETS[event.code][event.shiftKey][0]+ selectNodes[0].textContent.slice(offset[0])
  101. offset[0]+= 1;
  102. // 在前括號加上去後, 如果後括號的位置也在同一元素之中, 則也會向後偏移
  103. offset[1]+= (selectNodes[0].isEqualNode(selectNodes[1]))? 1: 0;
  104. selectNodes[1].textContent= selectNodes[1].textContent.slice(0,offset[1])+ BRACKETS[event.code][event.shiftKey][1]+ selectNodes[1].textContent.slice(offset[1])
  105. // 在更改文字內容後, 反白範圍會亂掉, 重新選取
  106. selection.collapse(selectNodes[0], offset[0]);
  107. selection.extend(selectNodes[1], offset[1]);
  108. }
  109. event.preventDefault();
  110.  
  111. // 中文輸入法時會出錯, ex: a|bc|d +[(] -> a(|bc|)d -> a(()d, 反白的區域也會變成前括號
  112. if(event.key == "Process"){
  113. setTimeout(() => {
  114. document.execCommand('undo');
  115. }, 10)
  116. }
  117. })
  118. })
  119. }
  120. autoBrackets("html");
  121. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址