Unfix Fixed Position Element on Firefox Context Menu

Add new item to top of right-click context menu to override position:fixed on elements in a page.

  1. // ==UserScript==
  2. // @name Unfix Fixed Position Element on Firefox Context Menu
  3. // @description Add new item to top of right-click context menu to override position:fixed on elements in a page.
  4. // @author Jefferson "jscher2000" Scher
  5. // @namespace JeffersonScher
  6. // @copyright Copyright 2016 Jefferson Scher
  7. // @license BSD 3-clause
  8. // @include *
  9. // @version 0.5.1
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. // Context menu options -- Firefox only, and do not replace any existing context menu! Also, do not run in iframes
  14. if (!document.body.hasAttribute("contextmenu") && "contextMenu" in document.documentElement && (window.self == window.top)) {
  15. var cmenu = document.createElement("menu");
  16. cmenu.id = "UFPEcontext";
  17. cmenu.setAttribute("type", "context");
  18. cmenu.innerHTML = '<menu id="UFPEFlyout" label="Unfix Fixed Element" ufpeundolist="">' +
  19. '<menuitem id="UFPEStatic" label="Make Static"></menuitem>' +
  20. '<menuitem id="UFPEHide" label="Hide"></menuitem>' +
  21. '<menuitem id="UFPEUnHide" label="UnHide"></menuitem>' +
  22. '</menu>';
  23. document.body.appendChild(cmenu);
  24. document.getElementById("UFPEStatic").addEventListener("click", UFPE_doStyle, false);
  25. document.getElementById("UFPEHide").addEventListener("click", UFPE_doStyle, false);
  26. document.getElementById("UFPEUnHide").addEventListener("click", UFPE_dispUnHide, false);
  27. // attach menu and create event for filtering
  28. document.body.setAttribute("contextmenu", "UFPEcontext");
  29. document.body.addEventListener("contextmenu", UFPE_cmenuFilter, false);
  30. } else {
  31. console.log("DIDN'T ADD CONTEXT MENU!");
  32. }
  33. function UFPE_cmenuFilter(e){
  34. // Mark the right-clicked element for later reference (is there an easier way?)
  35. var tgt = e.target;
  36. // Compute and store a highly unique element based on the current time
  37. var dNew = new Date();
  38. tgt.setAttribute('unfixtime', dNew.getTime());
  39. // Store that same time on the first context menu item for each of reference
  40. document.getElementById('UFPEFlyout').setAttribute('unfixtgt', tgt.getAttribute('unfixtime'));
  41. }
  42. function UFPE_doStyle(e){
  43. // What is the action?
  44. var axn = e.target.id.substr(4); // Static or Hide
  45. // Find the right-clicked element using the stored timestamp -- should be unique
  46. var tgtval = document.getElementById('UFPEFlyout').getAttribute('unfixtgt');
  47. var tgt = document.querySelector('[unfixtime="'+tgtval+'"]');
  48. if (!tgt){
  49. // Oh crap, something went wrong
  50. alert('Unable to find my target!');
  51. return;
  52. }
  53. // Find the position:fixed element -- it could be the right-clicked element or an ancestor containing it
  54. while (tgt.nodeName != 'BODY') {
  55. var posval = window.getComputedStyle(tgt,null).getPropertyValue('position');
  56. // For debugging purposes
  57. // console.log('tgt.nodeName='+tgt.nodeName+' has '+posval);
  58. if (posval=='fixed' || (posval=='static' && tgt.hasAttribute('ufpestatic') && axn=='Hide')){
  59. // This is the nearest ancestor with position:fixed
  60. switch (axn){
  61. case 'Static':
  62. // Set position:static and store a timestamp for reference
  63. tgt.style.position = 'static';
  64. tgt.setAttribute('ufpestatic', tgtval);
  65. return;
  66. case 'Hide':
  67. // Set a timestamp for reference and add it to the undo list
  68. tgt.setAttribute('ufpehidden', tgtval);
  69. tgt.setAttribute('ufpedisplay', window.getComputedStyle(tgt,null).getPropertyValue('display'));
  70. var hidden = document.getElementById('UFPEFlyout').getAttribute('ufpeundolist');
  71. if (hidden.length === 0) hidden = tgtval;
  72. else hidden += ',' + tgtval;
  73. document.getElementById('UFPEFlyout').setAttribute('ufpeundolist', hidden);
  74. // Set display:none to hide the element
  75. tgt.style.display = 'none';
  76. return;
  77. }
  78. console.log('Value for axn is corrupted!');
  79. break;
  80. } else {
  81. // Check the next higher ancestor
  82. tgt = tgt.parentNode;
  83. }
  84. }
  85. }
  86. function UFPE_dispUnHide(e){
  87. // Retrieve undo list
  88. var hidden = document.getElementById('UFPEFlyout').getAttribute('ufpeundolist');
  89. if (hidden.length === 0){
  90. alert('Nothing is hidden right now');
  91. return;
  92. }
  93. // Split off the last timestamp in the list
  94. var cpos = hidden.lastIndexOf(',');
  95. if (cpos === -1){
  96. tgtval = hidden;
  97. hidden = '';
  98. } else {
  99. tgtval = hidden.substr(cpos+1);
  100. hidden = hidden.substr(0, cpos);
  101. }
  102. // Update the undo list
  103. document.getElementById('UFPEFlyout').setAttribute('ufpeundolist', hidden);
  104. // Seek the hidden element
  105. var tgt = document.querySelector('[ufpehidden="'+tgtval+'"]');
  106. if (!tgt){
  107. // Oh crap, something went wrong
  108. alert('The hidden item seems to be gone. Maybe try again?');
  109. return;
  110. } else {
  111. // Restore the previous value of the display property
  112. tgt.style.display = tgt.getAttribute('ufpedisplay');
  113. }
  114. }