I want to recreate the effect shown in this fiddle
According to StackOverflow rules I apparently have to present some code if I link to jsfiddle.net, so here's the main function from that link. Although to see the effect you obviously have to follow the link instead.
$(document).ready(function() {
$(".textWrapper").hover(function() {
$(".highlight", this).show();
$(this).mousemove(function(e) {
var relativePos = e.pageY - this.offsetTop;
var textRow = (Math.ceil(relativePos / 18) * 18) - 18;
if (textRow >= 0) { $(".highlight", this).css("top", textRow + "px"); }
});
}, function() {
$(".highlight", this).hide();
});
});
Rather than highlight the text in yellow I'd prefer to change the color of the text itself.
I'd like the text to be light grey, and darken when highlighted, to bring that line into focus. This seems a lot more difficult than simply changing CSS, because the actual text properties do not change.
How do I accomplish this?
Take a look:
http://jsfiddle.net/5nxr6my4/
Using the same principle I created 2 white opaque divs #highTop and #highBot in order to overlay the text when the mouse pointer hovers over it. Their height and top properties are adjusted to the pointer position, so underlying black text appears gray, except the line at which the mouse pointer points to:
$(document).ready(function() {
$('#highTop').css('height', $('.textWrapper').height()).show();
$('.textWrapper').hover(function() {
$('#highBot').show();
$(this).mousemove(function(e) {
var relativePos = e.pageY - this.offsetTop;
var textRow = (Math.ceil(relativePos / 18) * 18) - 18;
if (textRow >= 0) {
// change height of #hightTop to make it cover upper part of text
$('#highTop').css('height', textRow + 'px');
// change position and height of #highBot to make it cover lower part of text
$('#highBot').css('height', $('.textWrapper').height() - 18 - textRow + "px")
.css('top', textRow + 18 + 'px');
}
});
}, function() {
// when the pointer goes out of the text, hide #highBot and make #highTop cover entire text
$('#highTop').css('height', $('.textWrapper').height() + 'px');
$('#highBot').hide();
});
});
I've done a little research and it appears that the only way that this would become possible would be to put each line in a separate HTML tag.
The reason that it is not possible the way you want it, is that the .highlight div doesn't contain the text itself so you can only apply an overlay rather than editing the underneath text.
It might help to have a look at http://jsbin.com/ukaqu3/91 which is about only displaying certain lines of text.
Related
So what I want to happen is that when viewing the Span the text is normal but as you scroll down it starts moving until it looks like such:
Before the effect:
While the effect occurs:
The header is represented by spans for each letter. In the initial state, the top pixel value for each is 0. But the idea as mentioned is that that changes alongside the scroll value.
I wanted to keep track of the scroll position through JS and jQuery and then change the pixel value as needed. But that's what I have been having trouble with. Also making it smooth has been another issue.
Use the mathematical functions sine and cosine, for characters at even and odd indices respectively, as the graphs of the functions move up and down like waves. This will create a smooth effect:
cos(x) == 1 - sin(x), so in a sense, each character will be the "opposite" of the next one to create that scattered look:
function makeContainerWiggleOnScroll(container, speed = 0.01, distance = 4) {
let wiggle = function() {
// y-axis scroll value
var y = window.pageYOffset || document.body.scrollTop;
// make div pseudo-(position:fixed), because setting the position to fixed makes the letters overlap
container.style.marginTop = y + 'px';
for (var i = 0; i < container.children.length; i++) {
var span = container.children[i];
// margin-top = { amplitude of the sine/cosine function (to make it always positive) } + { the sine/cosine function (to make it move up and down }
// cos(x) = 1 - sin(x)
var trigFunc = i % 2 ? Math.cos : Math.sin;
span.style.marginTop = distance + distance * trigFunc(speed * y)/2 + 'px';
}
};
window.addEventListener('scroll', wiggle);
wiggle(); // init
}
makeContainerWiggleOnScroll(document.querySelector('h2'));
body {
height: 500px;
margin-top: 0;
}
span {
display: inline-block;
vertical-align: top;
}
<h2>
<span>H</span><span>e</span><span>a</span><span>d</span><span>e</span><span>r</span>
</h2>
Important styling note: the spans' display must be set to inline-block, so that margin-top works.
Something like this will be the core of your JS functionality:
window.addEventListener('scroll', function(e) {
var scrl = window.scrollY
// Changing the position of elements that we want to go up
document.querySelectorAll('.up').forEach(function(el){
el.style.top = - scrl/30 +'px';
});
// Changing the position of elements that we want to go down
document.querySelectorAll('.down').forEach(function(el){
el.style.top = scrl/30 +'px';
});
});
We're basically listening in on the scroll event, checking how much has the user scrolled and then act upon it by offsetting our spans (which i've classed as up & down)
JSBin Example
Something you can improve on yourself would be making sure that the letters wont go off the page when the user scrolls a lot.
You can do this with simple math calculation, taking in consideration the window's total height and using the current scrollY as a multiplier.
- As RokoC has pointed out there is room for performance improvements.Implement some debouncing or other kinds of limiters
I've rather roughly built this website which uses an effect similar to the iOS Safari tab view to look at various pages of a virtual book. Everything is working fine apart from the fact that I can't centre each page in the visible viewport. For example if you scroll down to the final 'page' and click on it, it jumps to the top of the document, instead of staying in the centre of the visible viewport.
I think this is to do with the fact that the scrollable div uses overflow-y:scroll, but I just can't figure out for the life of me how to fix the problem.
Any help would be greatly appreciated!!
Here's my jQuery:
jQuery(document.body).on('click', '.page', function() { //Change to touchstart
// Generate number between 1 + 2
var randomClass = 3;
var randomNumber = Math.round(Math.random() * (randomClass - 1)) + 1;
// Initialise & Random Number
jQuery(this).addClass("activated").addClass('scaled-' + randomNumber);
// Exiting - Reset All
jQuery(document.body).on('click', '.activated', function() { //Change to Touchstart
jQuery(this).removeClass("activated scaled-1 scaled-2 scaled-3");
});
});
And here is a jsfiddle with all my code in it so you can get a better idea of what I'm trying to achieve.
https://jsfiddle.net/ontu1ngq/
Thanks!
You need to get the amount that #wrapper has been scrolled, so that you can use that to set the top of the .page accordingly. Then, when you remove the .activated class, you will just need to remove the inline top style.
jQuery(document.body).on('click', '.page', function() {
var randomClass = 3;
var randomNumber = Math.round(Math.random() * (randomClass - 1)) + 1;
jQuery(this).addClass("activated").addClass('scaled-' + randomNumber);
var wrapper_scrollTop = $("#wrapper").scrollTop(); //gets amount scrolled
var half_wrapper = $("#wrapper").height()*(0.5); //50% of wrapper height
jQuery(this).css("top",half_wrapper+wrapper_scrollTop);
jQuery(document.body).on('click', '.activated', function() {
jQuery(this).removeClass("activated scaled-1 scaled-2 scaled-3");
jQuery(this).css("top","") //returns top to original value specified in css
});
});
Check out this working fiddle: https://jsfiddle.net/tardhepc/1/
function jsiBoxAdjustTop()
{
var top
if ( jsiBox.preloadImg.height <= 699){
top = 216;
}
else{
top = 17;
}
jsiBox.boxNode.style.top = (top) + 'px';
}
I'm using that function to adjust a div's top position depending on the image that is in it's height. It's on a light box sort of script so every time I click the next button, a new image which could either be taller or smaller appears. It's working alright and it adjusts its position when the image is taller but my problem is it just jumps to that position. I'm really new to javascript so can anyone help me out to make this as if it's travelling/animating to it's position? I tried using setTimeOut but I think I was doing it wrong. I'd really love to know what I'm doing wrong.
Here's the full script if that helps. Link
you can use jQuery or YUI to do animate, such as
jQuery(jsiBox.boxNode).animate({'top': top}, 3000);
or you can write some simple code with setTimeout just for this case;
following code assume the begin top is 0.
var boxStyle = jsiBox.boxNode.style;
function animateTop(to) {
boxStyle.top = parseInt(boxStyle.top, 10) + 1 + 'px';
if (parseInt(boxStyle.top, 10) != to) {
setTimeout(function() {
animate(to);
}, 50);
}
}
animateTop(top);
I'm a bit stumped here. I am developing a feedback utility that will allow the user to "draw" boxes on a web page to highlight problem areas. Right now I have an overlay DIV that fills the screen and jQuery allows you to draw red outlined DIVs by clicking and dragging.
Here is the JS:
{
var $feedbackOverlay = jQuery('#feedbackOverlay');
var $original = { top: 0, left:0 };
$feedbackOverlay.bind('mousedown', function (e)
{
jQuery('<div id="currentHighlight"></div>')
.css('width', '1px')
.css('height', '1px')
.css('border', 'solid 3px #ff0000')
.css('border-radius', '5px')
.css('position', 'absolute')
.css('left', e.pageX)
.css('top', e.pageY)
.css('z-index', '8000001')
.appendTo('body');
$original = { top: e.pageY, left: e.pageX };
});
$feedbackOverlay.bind('mousemove', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
if ($currentHighlight.length > 0)
{
var $pos = { top: e.pageY, left: e.pageX };
if($pos.top < $original.top) $currentHighlight.css('top', $pos.top);
if ($pos.left < $original.left) $currentHighlight.css('left', $pos.left);
$currentHighlight.height(Math.abs($pos.top - $original.top));
$currentHighlight.width(Math.abs($pos.left - $original.left));
}
});
$feedbackOverlay.bind('mouseup', function (e)
{
var $currentHighlight = jQuery('#currentHighlight');
$currentHighlight.removeAttr('id');
});
var $feedbackInstructions = jQuery('#feedbackInstructions');
$feedbackInstructions.fadeIn(1000, function ()
{
setTimeout(function ()
{
$feedbackInstructions.fadeOut(1000);
}, 3000);
});
$feedbackOverlay.height(jQuery(document).height());
});
Here is a jsFiddle for the above:
http://jsfiddle.net/Chevex/RSYTq/
The problem is that I can't drag the boxes up or left. The first click puts the top left corner where the mouse clicked. After that subsequent dragging will change the width of the box. Letting go of the mouse completes the box and you may then start drawing another one. If you try to drag the DIV left or up while drawing it's width will remain at 0 but won't go negative.
Here you can find working solution: http://jsfiddle.net/RSYTq/34/
Something like this will get you closer to what you want: http://jsfiddle.net/RSYTq/18/
Doesn't quite handle move up and to the left and then switching to moving down and to the right quite right yet but it gives you the idea.
There's no such thing a a negative width - these are not coorindinates. You need to reposition and recalculate the corner positions relative to the corner that's not being moved.
Sounds like you need to check if the click origin (x,y) is > than the current mouse position, and then swap which one you use for the CSS top-left.
You would need to track the original start point somewhere (variables, data attributes on #currentHighlight, wherever you want), and check for width or height < 0. When so, set the #currentHighlight left/top CSS to be offset by original + (e.pageX - $currentHighlight.position().left) (for example). Then set the #currentHighlight width/height to the same difference (but positive: (e.pageX - $currentHighlight.position().left) * -1).
I have something vaguely like the following:
<div id="body">
surrounding text
<div id="pane" style="overflow: auto; height: 500px; width: 500px;">
lots and lots of text here
<span id="some_bit">tooltip appears below-right of here</span>
</div>
more surrounding text (should be overlapped by tooltip)
</div>
and:
<div id="tooltip" style="width: 100px; height: 100px;">Whee</div>
What I want to do is insert the tooltip such that it is positioned above the pane it's in. If it's attached to an element that's next to the pane boundary (like above), then it should be visible above the pane, and above the text surrounding the pane.
It should NOT a) extend the pane, such that you have to scroll down to see the tooltip (like in http://saizai.com/css_overlap.png), or b) be cut off, so you can't see all of the tooltip.
I'm inserting this with JS, so I can add a wrapper position:relative div or the like if needed, calculate offsets and make it position:absolute, etc. I would prefer to not assume anything about the pane's position property - the tooltip should be insertable with minimal assumptions of possible page layout. (This is just one example case.)
It's for a prototype tooltip library I'm writing that will be open source.
ETA: http://jsfiddle.net/vCb2y/5/ behaves visually like I want (if you keep re-hovering the trigger text), but would require me to update the position of the tooltip on all DOM changes and scrolling behavior. I would rather the tooltip be positioned with pure CSS/HTML so that it has the same visual behavior (i.e. it overlaps all other elements) but stays in its position relative to the target under DOM changes, scrolling, etc.
ETA 2: http://tjkdesign.com/articles/z-index/teach_yourself_how_elements_stack.asp (keep defaults except set cyan div 'a' to position:relative; imagine 'A' is the pane and 'a' the tooltip) seems to more closely behave as I want, but I've not been able to get it to work elsewhere. Note that if you make 'A' overflow: auto, it breaks the overlapping behavior of 'a'.
I can't think of a pure HTML/CSS solution for this.
The overflow declaration is the issue here. If the tooltip is in #pane:
you establish a positioning context within #pane, then the tooltip shows next to #some_bit (regardless of scrolling, etc.) but it gets cut-off.
you do not establish a positioning context, then the tooltip is not clipped but it has no clue where #some_bit is on the page.
I'm afraid you'll need JS to monitor where #some_bit is on the page and position the tooltip accordingly. You'd also need to kill that tooltip as soon as #some_bit is outside of the viewing area (not an issue if the trigger is mouseover).
Actually, if the trigger is mouseover then you may want to use the cursor coordinates to position the tooltip (versus calculating the position of #some_bit).
I would just put the tooltip outside of the #pane div and position it absolutely using JavaScript since you're using JS anyway.
I don't use Prototype so I don't know how it's done in Prototype, but in jQuery, you'd use $(element).position() to get the element position. If you have to do it manually, it's a little more complicated.
And you'll probably want to add a little extra logic to prevent the tooltip from extending outside of the document.
Edit: CSS used
#tooltip {
z-index: 9999;
display: none;
position: absolute;
}
JS used
Note: in jQuery, but it should be easy to change it to Prototype syntax.
$('#some_bit').hover(function() {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
// hovered element
var offset = $(this).offset();
var top = offset.top + docViewTop;
var left = offset.left;
var width = $(this).width();
var height = $(this).height();
var right = left + width;
var bottom = top + height;
// pane
var poffset = $('#pane').offset();
var ptop = poffset.top + docViewTop;
var pleft = poffset.left;
var pwidth = $('#pane').width();
var pheight = $('#pane').height();
var pright = pleft + pwidth;
var pbottom = ptop + pheight;
// tooltip
var ttop = bottom;
var tleft = right;
var twidth = $('#tooltip').width();
var theight = $('#tooltip').height();
var tright = tleft + twidth;
var tbottom = ttop + theight;
if (tright > pright)
tleft = pright - twidth;
if (tbottom > pbottom)
ttop = pbottom - theight;
if (tbottom > docViewBottom)
ttop = docViewBottom - theight;
$('#tooltip').offset({top: ttop, left: tleft});
$('#tooltip').css('display', 'block');
}, function() {
$('#tooltip').hide();
});
Edit: See it here.