I need to mix units in a dynamically generated div, but this doesn't render (ie left is still the default after that line is executed).
Any help appreciated.
var tab3 = document.createElement('div');
tab3.className = 'tab';
tab3.style.display = 'none';
tab2.style.left = 'calc(~"200px+40vw")px';
You've got several mistakes in your calc expression:
calc(~"200px+40vw")px` -> `calc(200px + 40vw)
According to #JohnWeisz's comment - calc(~"200px+40vw")px is a Less expression, ~"..." is used to prevent Less from processing the addition operation from the source stylesheet files, but that's both invalid syntax and unnecessary from CSS string context.
#G-Cyr adds that white space is required on both sides of the + and - operators. The * and / operators can be used without white space around them.
Example:
var tab3 = document.createElement('div');
tab3.className = 'tab';
tab3.style.left = 'calc(20px + 40vw)';
document.body.append(tab3);
.tab {
position: absolute;
width: 20vw;
height: 20vh;
background: red;
}
Related
I am trying to format quotes the old-fashioned way. — Is there any way to go from this input:
<p>He argued that <q>the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm</q> and concluded that…</p>
to this output (as displayed on the browser):
He argued that “the King by his procla-
“ mation or other ways cannot change any
“ part of the common law, or statute law,
“ or the customs of the realm” and con-
cluded that…
In the example above, hyphenation, alignment and line length are set arbitrarily and for illustration purpose. They aren’t of any concern.
I would like that, when a line breaks inside the <q>, each consequent lines (that are in the <q>), when displayed, be preceded by a quotation mark (so as the reader would visually isolate the quote). It is an old-fashioned way of formatting indirect speech.
The best I could come up with so far is an unsatisfactory pseudo-workaround using <blockquote> combined with text-shadow in CSS:
* {font-family: monospace; line-height: 1.4em; width: 25em; margin: 0;}
blockquote { position: relative; padding-left: 1.2em; text-indent: -1.2em; overflow-y: hidden;}
blockquote::before {content:"“"; position: absolute; top: 1.4em; left: 1.2em; text-shadow: 0 1.4em 0 #000, 0 2.8em 0 #000, 0 4.2em 0 #000;}
<p>He argued that <blockquote>“the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm”</blockquote> and concluded that…</p>
(Which may vaguely resemble what I wanted (kinda), but ultimately does not work given the block nature of <blockquote>; furthermore, when set inline, the desired effect of the quotation marks being set to the left is lost.)
Edit: acknowledging answers.
Obviously, we are going the JS route. I’ve considered:
Find browser-computed line breaks, and replace each one with "<br>“ "(eventually leading to multiple issues in justified texts—therefore not ideal);
#A-Haworth suggestion of “[a] method to find the line breaks [with] a dummy element, gradually add[ing] the text and spot[ting] when it gets higher.”
But I just don’t know how to do any of these things. Also, there must be a solution I haven’t thought of.
Any idea?
Last Edit (Feb 16, 2022)
No one, really?
It probably can't be done in an easy way. I tried to write a small function that might help you.
The solution needs to be finalized, but you can take the basic idea. if you need a dynamic solution, use resize observer, as indicated in the example.
function quote(el, leftPad = 16) {
const txt = el.textContent.trim().split(" ");
const ctx = document.createElement("canvas").getContext("2d");
const styleList = getComputedStyle(el);
ctx.font = `${styleList.fontWeight} ${styleList.fontSize} "${styleList.fontFamily}"`;
const maxWidth = el.clientWidth;
let paragraph = [];
let line = "";
let lineWidth = 0;
let blockquote = false;
const symbolMap = new Map();
let firstLineBreak = false;
let quoteRowCount = 0;
const getWordLength = (word) => {
let wordWidth = 0;
for (let i = 0; i < word.length; i++) {
if (!symbolMap.has(word[i])) {
symbolMap.set(word[i], ctx.measureText(word[i]).width);
}
wordWidth += symbolMap.get(word[i]);
}
return wordWidth;
};
for (let i = 0; i < txt.length; i++) {
const word = txt[i] + " ";
const width = getWordLength(word);
lineWidth += width;
if (txt[i].search('"') >= 0) blockquote = !blockquote;
if (lineWidth > maxWidth) {
paragraph.push(line);
if (!firstLineBreak && blockquote) {
line = '<br><span class="newline"></span>';
firstLineBreak = true;
quoteRowCount++;
} else {
if (blockquote) quoteRowCount++;
line = "";
}
lineWidth = leftPad + width;
}
line += word;
}
if (line.trim() !== "") paragraph.push(line);
el.innerHTML = paragraph.join("<wbr>");
const newLineElement = el.querySelector(".newline");
if (newLineElement) newLineElement.style.height = `${quoteRowCount}em`;
}
const el = document.querySelector("p");
quote(el);
// or if you need ResizeObserver =>
//new ResizeObserver(() => quote(el)).observe(el);
p {
width: 220px;
font-size: 16px;
font-family: Arial;
padding: 0;
box-sizing: content-box;
}
.newline {
display: block;
width: 1em;
padding-right: 1em;
float: left;
overflow: hidden;
box-sizing: border-box;
word-break: break-word;
}
.newline:before {
content: '""""""""""""""""""""""""';
}
<p>
He argued that "the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm" and concluded that…
</p>
Interesting question - how much can be done by CSS?
While I think for a totally general answer will need JS - slowly adding text to the paragraph and looking to see whether the height has changed or not - here's a snippet that does it mainly by CSS using pseudo element. However, as the browser uses the pseudo element to put in the quotation marks itself, these have to be placed in the text if they are still required.
It requires a way of getting the text that is in the paragraph before the q element into the content of the pseudo element also. Here this is done with a data attribute. Space is reserved for the column of double quotes by doing a text-indent on the paragraph.
So, not a totally general solution, but may point to a way of making it general using a bit of JS to do the content setting up initially.
p {
margin: 50px;
padding-left: 20px;
position: relative;
text-indent: -10px;
}
q {
position: relative;
}
q::before {
content: attr(data-start) '\a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " \a " ';
position: absolute;
top: 0;
left: 0;
transform: translateX(-100%);
height: 100%;
width: auto;
z-index: -1;
text-indent: 0;
white-space: pre;
padding-left: 20px;
overflow: hidden;
}
<p>He argued that <q data-start="He argued that ">"the King by his proclamation or other ways cannot change any part of the common law, or statute law, or the customs of the realm"</q> and concluded that…<br>some more text here for test</p>
I have a block with a few inline-blocks inside. These all have varying heights and widths. I have line wrap enabled.
What I'm trying to do is if I have a reference to one of the inline-blocks to
find out what line it is in and
how high that line is. (I can't change the HTML)
2 has the obvious solution of getting the bounding boxes of all blocks in the line and using the highest height as the height of the line. Here I'm just wondering if there is a more performant solution for that.
My main interest lies in a). My idea of an incredibly imperformant solution would be doing b) for all lines in the parent block and then comparing the offset-top of the searched element to the line-heights. I was hoping maybe someone can come up with a better idea?
edit:
relevant part of the html, css and js i'm using:
<div>
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</div>
div {
display: block;
background-color: yellow;
height: 600px;
width: 2000px;
}
div > div {
display: inline-block;
padding: 10px;
margin: 5px;
}
document.addEventListener("DOMContentLoaded", function() {
const nodes = document.querySelectorAll("div > div");
for(let i = 0; i < nodes.length; i++) {
nodes[i].style.width = `${5 + Math.random() * 200}px`;
nodes[i].style.height = `${5 + Math.random() * 200}px`;
nodes[i].style.backgroundColor = `hsl(${(Math.random() * 360)}, 100%, 50%)`;
}
const nodeiwanttogetdescribeddataof = nodes[4];
//code to get the data
});
i did not implement the ideas described as i was hoping to not need to. the purpose of the question was to find out whether there is some easy way to do what i need, for example (pseudocode) let theline = node.line.
Now that Microsoft has decided to move the #-ms-viewport rule in Edge behind an about:config flag, using javascript (not jquery) how do you determine if the user has that flag enabled (or is using an older version of Edge where it was still supported)?
Thread about the withdrawal of the rule here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7970618/
Maybe something like this? Please see comments in code.
function testSupportMsViewport() {
let result = false;
let s = document.createElement('style');
let test = document.createElement('div');
// create style rule to make viewport 200px in width
// if #-ms-viewport is interpreted
s.innerHTML = '#-ms-viewport { width: 200px; }';
// hide any overflow hence we do not need to take scrollbars into account
s.innerHTML += '* { overflow-y: hidden; }';
// use fixed position, block box sizing and no paddings/margins/borders
// together with a width of 1% (which is 2px when the viewport is 200px)
s.innerHTML += '.foo { position: fixed; display: block; padding: 0; border: 0; margin: 0; width: 1%; }';
test.classList += 'foo';
// append the style element and the test div to the html element
// applying the test styles to the div
// note that we're not appending it to body since there may be
// styles applied which could disturb calculations
document.documentElement.appendChild(s);
document.documentElement.appendChild(test);
// check width of test div. If the browser interprets the rule,
// it should be 2 (2px)
// obviously, this yields a false-positive when the viewport really is just 200px in width. Think about using some other value or add additional checks
if (parseFloat(window.getComputedStyle(test).width) === 2) {
result = true;
}
// Remove elements since they're not needed anymore
document.documentElement.removeChild(s);
document.documentElement.removeChild(test);
return result;
}
I was not able to test that snippet in Edge since I'm not using Windows. However, the function returns true if the viewport is 200px in width and false otherwise.
(If you're targeting ES5, you may replace the let by var)
This question already has answers here:
How do I get the (x, y) pixel coordinates of the caret in text boxes?
(2 answers)
Closed 8 years ago.
In my project I'm trying to get the offset position of the caret in a textarea in pixels. Can this be done?
Before asking here, I have gone through many links, especially Tim Down's, but I couldn't find a solution which works in IE8+, Chrome and Firefox. It seems Tim Down is working on this.
Some other links which I have found have many issues like not finding the top offset of the caret position.
I am trying to get the offset position of the caret because I want to show an auto-complete suggestion box inside the textarea by positioning it based on the offset position of the caret.
PS: I can't use a contenteditable div because I have written lots of code related to a textarea.
You can create a separate (invisible) element and fill it with textarea content from start to the cursor position. Textarea and the "clone" should have matching CSS (font properties, padding/margin/border and width). Then stack these elements on top of each other.
Let me start with a working example, then walk through the code: http://jsfiddle.net/g7rBk/
Updated Fiddle (with IE8 fix)
HTML:
<textarea id="input"></textarea>
<div id="output"><span></span></div>
<div id="xy"></div>
Textarea is self-explanatory. Output is a hidden element to which we'll pass text content and make measures. What's important is that we'll use an inline element. the "xy" div is just an indicator for testing purposes.
CSS:
/* identical styling to match the dimensions and position of textarea and its "clone"
*/
#input, #output {
position:absolute;
top:0;
left:0;
font:14px/1 monospace;
padding:5px;
border:1px solid #999;
white-space:pre;
margin:0;
background:transparent;
width:300px;
max-width:300px;
}
/* make sure the textarea isn't obscured by clone */
#input {
z-index:2;
min-height:200px;
}
#output {
border-color:transparent;
}
/* hide the span visually using opacity (not display:none), so it's still measurable; make it break long words inside like textarea does. */
#output span {
opacity:0;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* the cursor position indicator */
#xy {
position:absolute;
width:4px;
height:4px;
background:#f00;
}
JavaScript:
/* get references to DOM nodes we'll use */
var input = document.getElementById('input'),
output = document.getElementById('output').firstChild,
position = document.getElementById('position'),
/* And finally, here it goes: */
update = function(){
/* Fill the clone with textarea content from start to the position of the caret. You may need to expand here to support older IE [1]. The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
*/
output.innerHTML = input.value.substr( 0, input.selectionStart ).replace(/\n$/,"\n\001");
/* the fun part!
We use an inline element, so getClientRects[2] will return a collection of rectangles wrapping each line of text.
We only need the position of the last rectangle.
*/
var rects = output.getClientRects(),
lastRect = rects[ rects.length - 1 ],
top = lastRect.top - input.scrollTop,
left = lastRect.left+lastRect.width;
/* position the little div and see if it matches caret position :) */
xy.style.cssText = "top: "+top+"px;left: "+left+"px";
}
[1] Caret position in textarea, in characters from the start
[2] https://developer.mozilla.org/en/docs/DOM/element.getClientRects
Edit: This example only works for fixed-width textarea. To make it work with user-resizable textarea you'd need to add an event listener to the resize event and set the #output dimensions to match new #input dimensions.
Here's an approach using rangyinputs, rangy and jQuery.
It basically copies the whole text from inside the textarea into a div of the same size. I have set some CSS to ensure that in every browser, the textarea and the div wrap their content in exactly the same way.
When the textarea is clicked, I read out at which character index the caret is positioned, then I insert a caret span at the same index inside the div. By only doing that I ended up having an issue with the caret span jumping back to the previous line if the user clicked at the start of a line. To fix that I check if the previous character is a space (which would allow a wrap to occur), if that is true, I wrap it in a span, and I wrap the next word (the one directly after the caret position) in a span. Now I compare the top values between these two span's, if they differ, there was some wrapping going on, so I assume that the top and the left value of the #nextword span are equivalent to the caret position.
This approach can still be improved upon, I'm sure I haven't thought of everything that could possibly go wrong, and even if I have, then I haven't bothered implementing a fix for all of them as I don't have the time to do so at the moment, a number of things that you would need to look at:
it doesn't yet handle hard returns inserted with Enter (fixed)
positioning breaks when entering multiple spaces in a row (fixed)
I think hyphens would allow a content wrap to occur as well..
Currently it works exactly the same way across browsers here on Windows 8 with the latest versions of Chrome, Firefox, IE and Safari. My testing has not been very rigorous though.
Here's a jsFiddle.
I hope it will help you, at the very least it might give you some ideas to build on.
Some Features:
I have included a ul for you which is positioned in the right spot, and fixed a Firefox issue where the textarea selection was not re-set back to its original spot after the DOM manipulations.
I have added IE7 - IE9 support and fixed the multiple word selection issue pointed out in the comments.
I have added support for hard returns inserted with Enter and multiple spaces in a row.
I have fixed an issue with the default behaviour for the ctrl+shift+left arrow text selection method.
JavaScript
function getTextAreaXandY() {
// Don't do anything if key pressed is left arrow
if (e.which == 37) return;
// Save selection start
var selection = $(this).getSelection();
var index = selection.start;
// Copy text to div
$(this).blur();
$("div").text($(this).val());
// Get current character
$(this).setSelection(index, index + 1);
currentcharacter = $(this).getSelection().text;
// Get previous character
$(this).setSelection(index - 1, index)
previouscharacter = $(this).getSelection().text;
var start, endchar;
var end = 0;
var range = rangy.createRange();
// If current or previous character is a space or a line break, find the next word and wrap it in a span
var linebreak = previouscharacter.match(/(\r\n|\n|\r)/gm) == undefined ? false : true;
if (previouscharacter == ' ' || currentcharacter == ' ' || linebreak) {
i = index + 1; // Start at the end of the current space
while (endchar != ' ' && end < $(this).val().length) {
i++;
$(this).setSelection(i, i + 1)
var sel = $(this).getSelection();
endchar = sel.text;
end = sel.start;
}
range.setStart($("div")[0].childNodes[0], index);
range.setEnd($("div")[0].childNodes[0], end);
var nextword = range.toHtml();
range.deleteContents();
var position = $("<span id='nextword'>" + nextword + "</span>")[0];
range.insertNode(position);
var nextwordtop = $("#nextword").position().top;
}
// Insert `#caret` at the position of the caret
range.setStart($("div")[0].childNodes[0], index);
var caret = $("<span id='caret'></span>")[0];
range.insertNode(caret);
var carettop = $("#caret").position().top;
// If preceding character is a space, wrap it in a span
if (previouscharacter == ' ') {
range.setStart($("div")[0].childNodes[0], index - 1);
range.setEnd($("div")[0].childNodes[0], index);
var prevchar = $("<span id='prevchar'></span>")[0];
range.insertNode(prevchar);
var prevchartop = $("#prevchar").position().top;
}
// Set textarea selection back to selection start
$(this).focus();
$(this).setSelection(index, selection.end);
// If the top value of the previous character span is not equal to the top value of the next word,
// there must have been some wrapping going on, the previous character was a space, so the wrapping
// would have occured after this space, its safe to assume that the left and top value of `#nextword`
// indicate the caret position
if (prevchartop != undefined && prevchartop != nextwordtop) {
$("label").text('X: ' + $("#nextword").position().left + 'px, Y: ' + $("#nextword").position().top);
$('ul').css('left', ($("#nextword").position().left) + 'px');
$('ul').css('top', ($("#nextword").position().top + 13) + 'px');
}
// if not, then there was no wrapping, we can take the left and the top value from `#caret`
else {
$("label").text('X: ' + $("#caret").position().left + 'px, Y: ' + $("#caret").position().top);
$('ul').css('left', ($("#caret").position().left) + 'px');
$('ul').css('top', ($("#caret").position().top + 14) + 'px');
}
$('ul').css('display', 'block');
}
$("textarea").click(getTextAreaXandY);
$("textarea").keyup(getTextAreaXandY);
HTML
<div></div>
<textarea>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.</textarea>
<label></label>
<ul>
<li>Why don't you type this..</li>
</ul>
CSS
body {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
}
textarea, div {
font-family: Verdana;
font-size: 12px;
line-height: 14px;
width: 300px;
display: block;
overflow: hidden;
border: 1px solid black;
padding: 0;
margin: 0;
resize: none;
min-height: 300px;
position: absolute;
-moz-box-sizing: border-box;
white-space: pre-wrap;
}
span {
display: inline-block;
height: 14px;
position: relative;
}
span#caret {
display: inline;
}
label {
display: block;
margin-left: 320px;
}
ul {
padding: 0px;
margin: 9px;
position: absolute;
z-index: 999;
border: 1px solid #000;
background-color: #FFF;
list-style-type:none;
display: none;
}
#media screen and (-webkit-min-device-pixel-ratio:0) {
span {
white-space: pre-wrap;
}
}
div {
/* Firefox wrapping fix */
-moz-padding-end: 1.5px;
-moz-padding-start: 1.5px;
/* IE8/IE9 wrapping fix */
padding-right: 5px\0/;
width: 295px\0/;
}
span#caret
{
display: inline-block\0/;
}
There's a much simpler solution for getting the caret position in pixels, than what's been presented in the other answers.
Note that this question is a duplicate of a 2008 one, and I've answered it here. I'll only maintain the answer at that link, since this question should have been closed as duplicate years ago.
Copy of the answer
I've looked for a textarea caret coordinates plugin for meteor-autocomplete, so I've evaluated all the 8 plugins on GitHub. The winner is, by far, textarea-caret-position from Component.
Features
pixel precision
no dependencies whatsoever
browser compatibility: Chrome, Safari, Firefox (despite two bugs it has), IE9+; may work but not tested in Opera, IE8 or older
supports any font family and size, as well as text-transforms
the text area can have arbitrary padding or borders
not confused by horizontal or vertical scrollbars in the textarea
supports hard returns, tabs (except on IE) and consecutive spaces in the text
correct position on lines longer than the columns in the text area
no "ghost" position in the empty space at the end of a line when wrapping long words
Here's a demo - http://jsfiddle.net/dandv/aFPA7/
How it works
A mirror <div> is created off-screen and styled exactly like the <textarea>. Then, the text of the textarea up to the caret is copied into the div and a <span> is inserted right after it. Then, the text content of the span is set to the remainder of the text in the textarea, in order to faithfully reproduce the wrapping in the faux div.
This is the only method guaranteed to handle all the edge cases pertaining to wrapping long lines. It's also used by GitHub to determine the position of its # user dropdown.
JsFiddle of working example: http://jsfiddle.net/42zHC/2/
Basically, we figure out how many columns fit in the width (since it will be monospace). We have to force scrollbars to always be there otherwise the calculation is off. Then we divide the number of columns that fit with the width, and we get the x offset per character. Then we set the line height on the textarea. Since we know how many characters are in a row, we can divide that with the number of characters and we get the row number. With the line height, we now have the y offset. Then we get the scrollTop of the textarea and subtract that, so that once it starts using the scrollbar, it still shows up in the right position.
Javascript:
$(document).ready(function () {
var cols = document.getElementById('t').cols;
var width = document.getElementById('t').clientWidth;
var height = $('textarea').css('line-height');
var pos = $('textarea').position();
$('#t').on('keyup', function () {
el = document.getElementById("t");
if (el.selectionStart) {
selection = el.selectionStart;
} else if (document.selection) {
el.focus();
var r = document.selection.createRange();
if (r == null) {
selection = 0;
}
var re = el.createTextRange(),
rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
selection = rc.text.length;
} else { selection = 0 }
var row = Math.floor((selection-1) / cols);
var col = (selection - (row * cols));
var x = Math.floor((col*(width/cols)));
var y = (parseInt(height)*row);
$('span').html("row: " + row + "<br>columns" + col + "<br>width: " + width + "<br>x: " + x +"px<br>y: " + y +"px<br>Scrolltop: "+$(this).scrollTop()).css('top',pos.top+y-$(this).scrollTop()).css('left',pos.left+x+10);
});
});
HTML:
<textarea id="t"></textarea>
<br>
<span id="tooltip" style="background:yellow"></span>
CSS:
textarea {
height: 80px;
line-height: 12px;
overflow-y:scroll;
}
span {
position: absolute;
}
I couldn't get something similar to work, so my solution was to locate the character position of the caret in the textarea, cut out the current paragraph and display this next to the textarea.
Using the offset, I placed a fake cursor (div, display:inline, 1px wide, border-left: 1px solid black) in this view of the editable text.
This way, you can create a visual feedback area where you can show the result of effects (quite like stackoverflow does when you write an answer).
I'm working on a userscript for Unicreatures that keeps track of events during exploration. It's working (well, it works but still needs some work lol) but I need a way to display the information I'm collecting that doesn't involve popup alerts for every step.
How can I create a box on the page and display stuff in it?
I'm looking to create a frame, window, whatever on the left side of the menu on this page, and write the values of various variables into it as my script runs.
I'm collecting this data in localStorage, so my script will first update various localStorage properties and then display the results in this box somehow:
localStorage.steps = Number(localStorage.steps) + 1;
displayValueInFloatingBox(localStorage.steps + ' in Sargasso' );
I'd also like to add a button to this box to reset the values of these properties, so that I have a choice of keeping track forever or just for a session or two without having to edit the script every time (especially if I decide to share the script). I assume this would just set the variables to zero so I just need to know how to create the button and making it do something... This would probably use an eventListener somehow.
Please stick to plain JavaScript, no jQuery... I'm still barely getting JavaScript at the moment. And please, explain answers so I understand how something works - I don't just want code snippets that leave me coming back with a similar question because I don't understand why a bit of code was used.
Appendix A: my current script
// ==UserScript==
// #name Unicreatures Egg pop-up
// #namespace Unicreatures Egg pop-up
// #description Unicreatures Egg pop-up
// #include http://unicreatures.com/explore.php*
// #include http://www.unicreatures.com/explore.php*
// ==/UserScript==
var regexp = /You find an? (Exalted )?(.*?) egg nearby/;
var match = regexp.exec( document.body.innerHTML );
if ( match ) {
if ( match[1] ) {
alert( "Exalted egg found: " + match[2] );
} else {
alert( "Normal egg found: " + match[2] );
}
}
var y = document.body.innerHTML;
var links = document.getElementsByTagName( 'a' );
for ( var i = 0; i < links.length; i++ ) {
var link = links[i];
if ( /area=sea(?!\&gather)/.test( link.href )) {
link.addEventListener( 'click', function () {
localStorage.steps=Number(localStorage.steps)+1
// alert(localStorage.steps + ' in Sargasso' );
}, true );
}
}
//document.addEventListener('click', function(){alert('page clicked')}, true);
if(y.indexOf("You find a Noble")> 0)
{
alert('Noble Egg Alert');
}
if(y.indexOf("You find an Exalted")> 0)
{
localStorage.exaltedEggCount=Number(localStorage.exaltedEggCount)+1;
alert('Exalted Egg Alert '+localStorage.exaltedEggCount);
}
if(y.indexOf("egg nearby!")> 0)
{
localStorage.eggCount=Number(localStorage.eggCount)+1;
alert('Egg Alert '+localStorage.eggCount);
}
Here's one simple way to add a box to the top left corner of the page. First, we need to create a div element to serve as the box. (Other HTML elements could work too, but div is a good choice since it has no special meaning, it's just a simple container.)
var box = document.createElement( 'div' );
We'll give our box an ID, both so that we can find it later with document.getElementsById() and so that we can style it with CSS:
box.id = 'myAlertBox';
Now we need to style the box. Since we're using GreaseMonkey, we can use GM_addStyle to add CSS style rules to the document:
GM_addStyle(
' #myAlertBox { ' +
' background: white; ' +
' border: 2px solid red; ' +
' padding: 4px; ' +
' position: absolute; ' +
' top: 8px; left: 8px; ' +
' max-width: 400px; ' +
' } '
);
Note the awkward syntax for including a multi-line string in JavaScript. (There are other ways to style the box, too, which will work also outside GreaseMonkey. I'll show some of the them below.)
Looking at the CSS style rule itself, the first three lines just say that our box should have a white background and a two pixels wide red border, and that there should be four pixels of padding between the border and the content. All this just makes it look like a typical simple alert box.
The following line says that our box should be absolutely positioned on the page — that is, always in a fixed position regardless of what else is on the page — and the one below that specifies the position we want to give it: here, four pixels from the top left corner of the page. The last line just says that the box should not stretch to be more than 400 pixels wide, no matter how much content we stuff into it.
Speaking of which, of course we need to add some content too. We can either just use plain text:
box.textContent = "Here's some text! This is not HTML, so <3 is not a tag.";
or we can use HTML:
box.innerHTML = "This <i>is</i> HTML, so <3 needs to be escaped.";
Finally, we need to add the box to the page to make it show up:
document.body.appendChild( box );
And there you go! A box on the page.
OK, but how do we get rid of it? Well, the simplest way would be to just make it disappear when clicked:
box.addEventListener( 'click', function () {
box.parentNode.removeChild( box );
}, true );
Alternatively, we could create a separate close button for the box and set the click handler only for that:
var closeButton = document.createElement( 'div' );
closeButton.className = 'myCloseButton';
closeButton.textContent = 'X';
GM_addStyle(
' .myCloseButton { ' +
' background: #aaa; ' +
' border: 1px solid #777; ' +
' padding: 1px; ' +
' margin-left: 8px; ' +
' float: right; ' +
' cursor: pointer; ' +
' } '
);
box.insertBefore( closeButton, box.firstChild );
closeButton.addEventListener( 'click', function () {
box.parentNode.removeChild( box );
}, true );
Inserting the close button before any other content in the box, and giving it the style float: right makes it float to the top right corner and makes text flow around it. The cursor: pointer rule makes the mouse cursor look like a hand when over the button, showing that it's clickable.
You can also add other buttons to the box (or elsewhere on the page) in the same way.
I gave the button a class name instead of an ID so that, if you want, you can give all your buttons the same class and they'll be style the same way.
It's also possible to just put the HTML code for the buttons in box.innerHTML, find the resulting elements e.g. with box.getElementById() and add the click handlers for them that way.
I said I'd mention other ways of styling elements. One simple way is to just write the CSS rules directly into its style property:
box.style.cssText =
' background: white; ' +
' border: 2px solid red; ' +
' padding: 4px; ' +
' position: absolute; ' +
' top: 8px; left: 8px; ' +
' max-width: 400px; ' ;
(This way, we wouldn't even need to give the box an ID.) It's also possible to set (and read) the styles one at a time:
box.style.background = 'white';
box.style.border = '2px solid red';
box.style.padding = '4px';
box.style.position = 'absolute';
box.style.top = '8px';
box.style.left = '8px';
box.style.maxWidth = '400px';
You'll note that some of the names are different; for example, max-width would not be a valid JavaScript property name, so it becomes maxWidth instead. The same rule works for other CSS property names with hyphens.
Still, I prefer GM_addStyle because it's more flexible. For example, if you wanted to make all links inside your box red, you could just do:
GM_addStyle(
' #myAlertBox a { ' +
' color: red; ' +
' } '
);
By the way, here's a neat trick: if you replace position: absolute with position: fixed, then the box will not scroll along with the page — instead it'll stay fixed to the corner of your browser even if you scroll down.
Another tip: If you don't have Firebug yet, install it. It will make examining page content and debugging JavaScript so much easier.