scroll to elements inside a div with fixed height - javascript

I have a <div> with a fixed height and overflow-y: scroll. Inside this div, I have primarily a <p> tag containg a long text with some highlithing (spans with background-color and a numbered id attribute).
By the way, it is a HTMl5 application with AngularJS.
Now I like to implement a button to jump through the highlighted positions: I like to get the div scrolled to the right position and the rest of the page shall stay untouched i.e. "unscrolled".
How can I achieve that the div is scrolled to the right position and not the whole page is scrolled down disturbing the page layout?
On principle, I know that I can use hashtag + id in the url to go to the element with a given id - I also found $anchorScroll from AngularJS but it seems not to be the right way, since it scrolles down the whole browser content instead of just inside my scrollable div.
Years ago, as using an iframe was not ugly, it was easy to use an iframe for this; however, today, I think there must be better solutions.
Any help is appreciated!
Thanks in advance.

Here's an example that might help you. It uses jQuery to set the scrollTop on a target <div> without scrolling the rest of the document.
To use it, enter a number between 0-99 in the <input> field and click Submit. The div should scroll to the top of the <span> element (within the target div) with that numeric id.
Note that the value in the call to scrollTop() is corrected for the height of the target element.
for (var i = 0; i < 100; i++) {
// http://www.paulirish.com/2009/random-hex-color-code-snippets/
var randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
$("#myScrollingDiv").append('<p>#' + i + ': text <span style="background-color:' + randomColor + '" id="' + i + '">text</span> text</p>')
}
$("#myForm").submit(function(event) {
event.preventDefault();
var idNumber = $("#idNumber").val()
var targetElem = $("#" + idNumber);
var targetOffset = targetElem.offset();
var divScrollTop = $('#myScrollingDiv').scrollTop();
$('#myScrollingDiv').scrollTop(divScrollTop + targetOffset.top - targetElem.height());
});
body {
height: 5000px;
overflow-y: scroll;
}
#myScrollingDiv {
overflow-y: scroll;
height: 100px;
width: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="myScrollingDiv"></div>
<form id="myForm">
<input id="idNumber" type="number">
<input type="submit">
</form>
Enter a numeric ID:

This question, if I understand it correctly, has been already answered here if you are willing to use JS and JQuery. Just attach the code suggestion to a button.
https://stackoverflow.com/a/5267018/5797159

Related

Javascript - Get element behind caret in child elements of [contenteditable]

I am building a simplistic and easy-to-use text editor in Javascript, basically a WYSIWYG editor. I will be using the contenteditable attribute to the main wrapper (.wrapper). When you press enter inside the .wrapper, a new <p> element with a unique id gets appended to the wrapper.
I need a way to fetch which child element of the .wrapper that is currently selected (i.e., being focused or having the caret/text marker inside of it).
I've searched for days without any results, and I've tried using document.elementFromPoint() but without any proper results.
When using $(":focus"), I get the entire .wrapper and not the specific child element.
Edit:
Example HTML structure:
<div class="container t-wrapper" contenteditable>
</div>
Example Javascript code:
$(document).ready(function() {
$currentElement = $("[contenteditable]");
$(".t-wrapper").each(function() {
var id = generateIdentifier(6); // Ignore this
/* This creates the initial <p> child element, where you start typing. */
$(this).html('<p class="t-empty t-first" id="' + id + '"></p>');
$(this).on("mouseup", function() {
/* $currentElement = whatever element the caret is inside. */
});
$(this).on("keyup", function() {
/* $currentElement = whatever element the caret is inside. */
});
));
});
Edit 2:
I managed to get it fairly working with the mouseup event, since you're actually clicking on something. But I need this to work when moving the caret using the keyboard. Alternatively, I need some way to get the position of the caret in pixels, and then use document.elementFromPoint() to get the specific element.
:focus doesn't select your elements because they are not focusable.
You can make them focusable by adding tabindex="-1" in HTML, or tabIndex = -1 in JS.
var generateIdentifier = Math.random;
var currentElement = document.querySelector("[contenteditable]");
[].forEach.call(document.querySelectorAll(".t-wrapper"), function(el) {
var first = document.createElement('p');
first.className = "t-empty t-first";
first.id = generateIdentifier(6);
first.textContent = 'Press enter to create new paragraphs';
first.tabIndex = -1;
el.appendChild(first);
});
.container > :focus {
border: 1px solid blue;
}
<div class="container t-wrapper" contenteditable></div>
It seems that if you add it to the first paragraph, new paragraphs obtain it automatically. But if you want to be sure, I guess you could use a mutation observer or a keyup event listener to detect paragraphs without tabindex, and add it:
el.addEventListener("keyup", function(e) {
var newChild = el.querySelector('p:not([tabindex])');
if(newChild) newChild.tabIndex = -1;
});
document.activeElement will return the currently focused element.

How to position a div next to element with absolute position?

I am trying to create a div that can be attached to an element whenever user hover to the link
I have many links and my codes look like the following.
for loops to create many links
codes....
link.href='#';
link.innerHTML = 'test'
link.onmouseover = OnHover;
codes....
function OnHover(){
var position;
var div = document.createElement('div');
div.className='testdiv';
div.innerHTML = 'test';
position=$(div).position();
div.style.top = position['top'] + 15 + 'px';
$(this).prepend(div);
}
link element1
link element2
----
| | //add new div when hover link element2
----
link element2
link element3
my css
.testdiv{
position:absolute;
}
I want to add a new div everytime the user hover to my link and position on the left top of the element.
My code would position all the div on top instead of every element.
Are there anyway to do this? Thanks so much!
Without seeing your other JavaScript/markup I can make the following observations:
You need to set position:absolute on your new div before top will do anything.
You need to make sure link is non-position:static.
Non-dynamic styles like the above should be in your CSS, not JS.
Positioning absolutely means you shouldn't need to use $(div).position()
You're using a mix of jQuery and pure JavaScript which looks a little odd :)
JS
function OnHover() {
var position;
var div = $('<div></div>');
div.addClass('testdiv');
div.html('test');
div.css('top', 15);
$(this).prepend(div);
}
CSS
.testdiv {
position:absolute;
}
a.testanchor {
position:relative;
}

Measuring height of an off page element

I'm Ajaxing in elements. I want to decide where to put them based on their height (think box-packing type algorithm).
When I loop through my elements as such, I always get 0 for the outerHeight:
posts.each(function(i, e) {
var elementHeight = $(e).outerHeight();
// elementHeight is always 0
});
How can I get the display height of my element?
It appears I have to add the element to the page to get the height, which makes sense.
What's the best way of doing this while being invisible to the user as simply as possible?
Append the posts to a hidden (display: none) div element and then iterate over them to get their respective outerHeight values.
HTML
<div id="postContainer"></div>
CSS
#postContainer { display: none;}
JS
var cont = $('#postContainer'), postsLength = posts.length;
posts.each(function(i, e) {
cont.append(e);
if (!--postsLength) calcOuterHeight();
});
function calcOuterHeight(){
cont.find('.posts').each(function(i, e){
var oh = $(e).outerHeight();
})
}
You have to add the elements to the page so that they go through the layout process, then they will have a size.
You can add them to the body, get the height, and then remove them. As the browser is busy running your code while the elements are in the page, the elements will never show up:
var el = $('<div>test</div>');
$(document.body).append(el);
var h = el.height();
el.remove();

How to point to or select an arbitary element with content in CKEditor?

I need to add CSS classes to basically any element that the user selects or points to in CKEditor. For example if there is a table that contains an list, my element structure and elementspath might look like this under the cursor:
body > table > tbody > tr > td > ul > li
Out of those, the user needs to be able to somehow select or point to the table, tr, td, ul or li element. After the user selected / pointed to the element they want to add a class to they would get a dialog to choose a class.
How would I detect which element the user wants to point to?
I'm open to any and all suggestions, even if that means hacking the core. For example I would think that this is doable with elementspath having a context menu where I could click something like "properties" for the element and that would bring up a dialog where the user could choose a class, but I have no idea how or even if it is possible to add a context menu to elementspath!
In Firefox this kind of works out of the box in that when a user clicks an element in elementspath I can GetSelectedElement and it returns what the user has clicked, but it doesn't work in IE/Chrome.
I kind hacked together a horrible but somewhat functional solution. In elementspath plugin.js I added
function onContextMenu(elementIndex, ev) {
editor.focus();
var element = editor._.elementsPath.list[elementIndex];
editor.execCommand('elementspathContextmenuForElement', ev, element);
}
var onContextMenuHanlder = CKEDITOR.tools.addFunction(onContextMenu);
And then where the elementspath item html is generated I added:
oncontextmenu="return CKEDITOR.tools.callFunction(', onContextMenuHanlder, ',', index, ', event );"
And then I made a plugin that creates a html "context menu"
CKEDITOR.plugins.add('elementspathcontextmenu', {
init: function (editor) {
editor.addCommand('elementspathContextmenuForElement', {
exec: function (editor, event, element) {
debug(element);
var tempX = event.pageX + 'px';
var tempY = event.pageY + 'px';
window.newdiv = document.createElement('div');
window.newdiv.setAttribute('id', "tmpContextMenuDiv");
window.newdiv.style.width = 300 + 'px';
window.newdiv.style.height = 300 + 'px';
window.newdiv.style.position = "absolute";
window.newdiv.style.left = tempX;
window.newdiv.style.top = tempY;
window.newdiv.innerHTML = '<p>Do something</p>';
document.body.appendChild(window.newdiv);
},
canUndo: false // No support for undo/redo
});
}
});
I feel a bit dirty hacking the core like that and creating a div element for the context menu in that way but it kind of works for me. This is by no means the final code but it gets the point across.

Table of content like JavaScript-Garden

I want to create a table of content similar to JavaScript Gardens. How do they determine which section is currently active and do you have any recommended JavaScript libraries that imlpement this behavior?
Edit: So the thing I am asking for is how to know which section currently is active on the screen while the user is scrolling so that I can highlight that section in the table of content.
You can detect when an element enters the viewport of your browser, and then highlight the corresponding menu entry.
By using Firebug in Firefox, you can see that they use the scrollTop property of the window to know what the user is looking at.
highlight: function () {
// get the scroll height
var scroll = this.page.window.scrollTop(),
articleID = this.names[this.names.length - 1].id;
// while our item are above the viewport, we enumerate
for (var i = 0, l = this.names.length; i < l; i++) {
if (this.names[i].offset > scroll) {
articleID = this.names[i - 1].id;
break;
}
}
// we've got the content to highlight, let's add classes and expand menu-entries
var sectionID = articleID.split('.')[0],
page = this.page,
nav = page.nav;
if (sectionID !== page.section) {
nav.filter('.nav_' + page.section).removeClass('active');
nav.filter('.nav_' + sectionID).addClass('active');
this.expand(sectionID);
page.section = sectionID;
}
if (articleID !== page.article) {
nav.find('a[href="#' + page.article + '"]').removeClass('active');
nav.find('a[href="#' + articleID + '"]').addClass('active');
page.article = articleID;
this.mobile(articleID);
}
}
During the initialization they find out what each part takes in height
init: function(attribute) {
this.heights = this.page.nav.find('ul').map(function(idx, ele) {
return $(this).outerHeight();
}).get();
From these two pieces of info, they can highlight the correct entry to what the user is looking at, by attaching the function to the scroll, resize, etc... events of the window.
You can do that via html and css. They use a hover style for each entry and then link to html content via named anchors. You can see that in the address bar when you click on link.
E.g.:
TOC Entry:
<code>hasOwnProperty</code>
Content Body:
<a name="object.hasownproperty"></a>
<!-- HTML Content here -->
Of course, if you want nice animation and stuff, use something like
http://www.position-absolute.com/articles/better-html-anchor-a-jquery-script-to-slide-the-scrollbar/ or http://css-plus.com/2011/03/plusanchor-jquery-plugin/
Update:
To Achieve highlighting (pseudocode):
Keep a tab of all your sections
Attach an onscroll event handler to the body
onscroll, check the scrollTop to each section's top
If match found, remove highlight class from previous TOC entry and add it to new TOC entry.
You can name your TOC anchors such a way that they match the section's id. Then you can easily retrieve corresponding TOC entry by just saying #id and add your class to it.

Categories

Resources