I dont know how to describe this but I'm sure you'll understand if you visit this link.
http://jsfiddle.net/pahnin/yN3xf/
I want to append text to a p element with javascript and I'm sucessful, but if the text contains a tag like <font> the tag is displayed as it is.
Should I add code to detect the html elements or it can be done any other means?
if I do add code which detect font tag how to add the tag back to the text??
You could simply replace var textcopied = $('.welcome').html(); with var textcopied = $('.welcome').text(); to extract the text without any HTML tags included. But then, of course, you won't get your tags back at all.
Update: A somewhat different approach uses jQuery animations to slide the entire title into view smoothly:
$(document).ready(function(){
var $title = $('.welcome');
var twidth = $title.width();
var theight = $title.height();
$title.css({
overflow: 'hidden',
width: 0,
whiteSpace: 'nowrap',
height: theight
}).animate({
width: twidth
}, 5000); // milliseconds
});
http://jsfiddle.net/mblase75/yN3xf/16/
You could do something like this. Once all the text has been written out, then you replace the whole html of welcome with the original text. It's not the best I admit.
http://jsfiddle.net/yN3xf/13/
$(document).ready(function() {
var htmlcopied = $('.welcome').html();
var textcopied = $('.welcome').text();
$('.welcome').text('');
function foobar(i) {
if (i < textcopied.length) {
$('.welcome').append(textcopied.charAt(i));
setTimeout(function() {
foobar(i + 1);
}, 80);
}
else {
$('.welcome').html(htmlcopied);
}
}
foobar(0);
});
UPDATE
This should give you the desired effect through different means. It has a div on top of the original text, and it slow reveals the text, which looks like it is being typed out.
Example
http://jsfiddle.net/guanome/LrbVy/
html
<div class="welcome">Hi, there <span class="hl">special text</span>, normal text
<div class="overlay"></div>
</div>
javascript
$(document).ready(function() {
$('.overlay').css('width', $('div.welcome').css('width'));
$('.overlay').css('height', $('div.welcome').css('height') + 15);
var origWidth = $('.overlay').css('width').replace(/px/g,'');
foobar(origWidth);
});
function foobar(i) {
if (i > -10) {
$('.overlay').css('width', i);
setTimeout(function() {
foobar(i - 10);
}, 80);
}
}
css
.hl{
color: red; font-family: helvetica;
background: #efefef;
color: black;
padding: 2px 7px;
-moz-box-shadow: 0 1px 1px #CCCCCC;
-webkit-box-shadow: 0 1px 1px #CCCCCC;
box-shadow: 0 1px 1px #CCCCCC;
}
div.welcome
{
position: relative;
width: 500px;
}
.overlay
{
position: absolute;
right: 0;
top: -3px;
width: 100%;
height: 25px;
background-color: #FFF;
z-index: 10;
}
UPDATE 2
With this change, the overlay will be added dynamically to the welcome message, the width doesn't have to be set, and it will work with multiple lines easily.
http://jsfiddle.net/guanome/LrbVy/4/
html
<div class="welcome">Hi, there <span class="hl">special text</span>, normal text</div>
javascript
$(document).ready(function() {
showWelcome();
});
function foobar(i, overlay) {
if (i > -10) {
overlay.css('width', i);
setTimeout(function() {
foobar(i - 10, overlay);
}, 80);
}
else {
overlay.remove();
}
}
function showWelcome() {
var welcome = $('div.welcome');
welcome.append('<div class="overlay"></div>');
welcome.css('position', 'relative');
var overlay = $('.overlay');
overlay.css({
'width': welcome.css('width'),
'height': (welcome.outerHeight() + 5),
'position': 'absolute',
'right': '0',
'top': '-3px',
'background-color': '#FFF',
'z-index': '10'
});
var origWidth = overlay.css('width').replace(/px/g, '');
foobar(origWidth, overlay);
}
You can also achieve this by iterating over the root element's contents(), and outputting individually each of the children nodes, one by one.
When treating each of contents elements, if it is a text node, it is enough to output all characters with a timeout. If it is not a text node, clone the node and append it to the target element. All characters inside it can be appended in the same way.
See it working here: http://jsfiddle.net/yN3xf/36/
$(document).ready(function(){
var $clonedContent = $('.welcome').clone();
$('.welcome').textContent = '';
$('.welcome').text('');
treatContents(0, $clonedContent, $('.welcome'));
function treatContents(num, container, target){
var $originalNode = container.contents()[num];
var $targetNode;
if ($originalNode.nodeType == 3){
$targetNode = target;
}
else{
$targetNode = $(container.contents()[num]).clone(false, false).appendTo(target);
$targetNode.text('');
}
$targetNode.textContent = '';
writeCharacters($originalNode.textContent , 0, $targetNode, num, container, target);
}
function writeCharacters(origText, x, target, contNum, contCont, contTarg) {
if(x<origText.length){
target.append(origText.charAt(x));
setTimeout(function() { writeCharacters(origText, x+1, target, contNum, contCont, contTarg); }, 80);
}
else{
treatContents(contNum+1, contCont, contTarg);
}
}
});
This sample could be adapted to allow nested tags, for instance:
<p class="welcome">Hi, there <b>bold text <i>bold italic text</i></b>, normal text</p>
Try replacing:
$('.welcome').append(textcopied.charAt(i));
with:
textHTML += textcopied.charAt(i);
$('.welcome').html(textHTML);
And at the begening of the code, put this:
var textHTML = '';
It works, but it doesn't look very good :P
Related
I'm trying to create my own "autocomplete", but when I type a letter (eg. w for word), then there's a splitsecond delay - enough to annoy the eye.
Here's my testcode:
CSS:
#txtSearchAutocomplete {
background-color: white !important;
position: absolute;
top: 0;
width: 100%;
font-size: 20px !important;
border: none !important;
color: gray;
}
#txtSearch {
background-color: transparent !important;
position: absolute;
top: 0;
width: 100%;
font-size: 20px !important;
border: none !important;
}
HTML:
<span style="position: relative; display: inline-block; width:100%; top: -18px;">
<input type="text" id="txtSearchAutocomplete" disabled >
<input type="text" id="txtSearch">
</span>
JS:
$(document).ready(function($) {
$("#txtSearch").focus();
$("#txtSearch").keyup(function(e) {
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var txt = $("#txtSearch").val().trim().toLowerCase();
$txtAutocomplete.val("");
if (txt == "") return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.val(entry);
break;
};
};
});
});
And a fiddle sample:
https://jsfiddle.net/25gwz1qu/1/
If you type in the letter w - delete it - type it again and so on, then you will notice a small delay. It might seam that the delay is a bit longer in IE.
Any idea how to get rid of this delay?
Thanks
The reason for the delay you are seeing is because the event triggers once the user lets go of the key. In that case, the oninput is the way to go. The event triggers when the textbox input changes.
$("#txtSearch").on('input', function(e) { ... })
Please take a look on my solution with comments that explain why I did those changes and here is a Working Fiddle.
On my machine the auto-complete is almost instant after those modifications.
$(document).ready(function($) {
// i had moved all selectors outside the function so the havy dom selection will happen only once
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var $searchElement = $("#txtSearch");
$searchElement.focus();
// In Jquery on works faster than on key up, cause user lets go of the key.
$searchElement.on('input',function(e) {
var txt = $searchElement.val().trim().toLowerCase();
// I had replaced the element to be a div and not a input cause the div element is much light weight and faster to draw for the browser
$txtAutocomplete.text("");
if (txt == "")
return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.text(entry);
break;
};
};
});
});
try this,
$(document).ready(function($) {
$("#txtSearch").focus();
$("#txtSearch").on('input',function(e) {
var autocomplete = ['word', 'excel'];
var $txtAutocomplete = $("#txtSearchAutocomplete");
var txt = $("#txtSearch").val().trim().toLowerCase();
$txtAutocomplete.val("");
if (txt == "") return;
for (i = 0; i < autocomplete.length; i++) {
var entry = autocomplete[i];
if (entry.indexOf(txt) == 0) {
$txtAutocomplete.val(entry);
break;
};
};
});
});
I am using the following to contain a div within borders.
The DIV is attached to each arrow key.
How can I change the background image of #body per each key-direction?
<script>
var pane = $('#border'),
box = $('#body'),
w = pane.width() - box.width(),
d = {},
x = 3;
function newv(v,a,b) {
var n = parseInt(v, 10) - (d[a] ? x : 0) + (d[b] ? x : 0);
return n < 0 ? 0 : n > w ? w : n;
}
$(window).keydown(function(e) { d[e.which] = true; });
$(window).keyup(function(e) { d[e.which] = false; });
setInterval(function() {
box.css({
left: function(i,v) { return newv(v, 37, 39); },
top: function(i,v) { return newv(v, 38, 40); }
});
}, 20);
</script>
<div id="border">
<div id="body">
<div class='head'></div>
</div>
</div>
#border{position:relative; width:300px; height:300px; border:2px solid red;}
#body{position:absolute; top:140px; left:140px; width: 70px; height: 70px; background: url('/images/model.png');}
#body .head{width: 70px; height: 25px; top: 0; background: rgba(0,0,0,0.5);}
Whenever any keypress/keydown events are fired change the background-image attribute using the css method of jquery.
$('.background').css('background-image','url(images/any_image.png)');
In your case it might be something like this,
$(window).keydown(function(e) {
d[e.which] = true;
$('#body').css('background-image','url(http://hdlatestwallpapers.com/wp-content/uploads/2013/12/Tom-and-Jerry-Cartoon-Wallpaper.jpg)');
});
Not sure how you are getting the images. If you can get random images on each keydown/keyup then you can change the url and have difference backgrounds. Same goes for keyup event.
PURE JS ONLY PLEASE - NO JQUERY
I have a div with overflow scroll, the window (html/body) never overflows itself.
I have a list of anchor links and want to scroll to a position when they're clicked.
Basically just looking for anchor scrolling from within a div, not window.
window.scrollTo etc. don't work as the window never actually overflows.
Simple test case http://codepen.io/mildrenben/pen/RPyzqm
JADE
nav
a(data-goto="#1") 1
a(data-goto="#2") 2
a(data-goto="#3") 3
a(data-goto="#4") 4
a(data-goto="#5") 5
a(data-goto="#6") 6
main
p(data-id="1") 1
p(data-id="2") 2
p(data-id="3") 3
p(data-id="4") 4
p(data-id="5") 5
p(data-id="6") 6
SCSS
html, body {
width: 100%;
height: 100%;
max-height: 100%;
}
main {
height: 100%;
max-height: 100%;
overflow: scroll;
width: 500px;
}
nav {
background: red;
color: white;
position: fixed;
width: 50%;
left: 50%;
}
a {
color: white;
cursor: pointer;
display: block;
padding: 10px 20px;
&:hover {
background: lighten(red, 20%);
}
}
p {
width: 400px;
height: 400px;
border: solid 2px green;
padding: 30px;
}
JS
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p'),
main = document.querySelector('main');
for (var i = 0; i < links.length; i++) {
links[i].addEventListener('click', function(){
var linkID = this.getAttribute('data-goto').slice(1);
for (var j = 0; j < links.length; j++) {
if(linkID === paras[j].getAttribute('data-id')) {
window.scrollTo(0, paras[j].offsetTop);
}
}
})
}
PURE JS ONLY PLEASE - NO JQUERY
What you want is to set the scrollTop property on the <main> element.
var nav = document.querySelector('nav'),
main = document.querySelector('main');
nav.addEventListener('click', function(event){
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
main.scrollTop = scrollTarget.offsetTop;
}
});
You'll notice a couple of other things I did different:
I used event delegation so I only had to attach one event to the nav element which will more efficiently handle clicks on any of the links.
Likewise, instead of looping through all the p elements, I selected the one I wanted using an attribute selector
This is not only more efficient and scalable, it also produces shorter, easier to maintain code.
This code will just jump to the element, for an animated scroll, you would need to write a function that incrementally updates scrollTop after small delays using setTimeout.
var nav = document.querySelector('nav'),
main = document.querySelector('main'),
scrollElementTo = (function () {
var timerId;
return function (scrollWithin, scrollTo, pixelsPerSecond) {
scrollWithin.scrollTop = scrollWithin.scrollTop || 0;
var pixelsPerTick = pixelsPerSecond / 100,
destY = scrollTo.offsetTop,
direction = scrollWithin.scrollTop < destY ? 1 : -1,
doTick = function () {
var distLeft = Math.abs(scrollWithin.scrollTop - destY),
moveBy = Math.min(pixelsPerTick, distLeft);
scrollWithin.scrollTop += moveBy * direction;
if (distLeft > 0) {
timerId = setTimeout(doTick, 10);
}
};
clearTimeout(timerId);
doTick();
};
}());
nav.addEventListener('click', function(event) {
var linkID,
scrollTarget;
if (event.target.tagName.toUpperCase() === "A") {
linkID = event.target.dataset.goto.slice(1);
scrollTarget = main.querySelector('[data-id="' + linkID + '"]');
scrollElementTo(main, scrollTarget, 500);
}
});
Another problem you might have with the event delegation is that if the a elements contain child elements and a child element is clicked on, it will be the target of the event instead of the a tag itself. You can work around that with something like the getParentAnchor function I wrote here.
I hope I understand the problem correctly now: You have markup that you can't change (as it's generated by some means you have no control over) and want to use JS to add functionality to the generated menu items.
My suggestion would be to add id and href attributes to the targets and menu items respectively, like so:
var links = document.querySelectorAll('a'),
paras = document.querySelectorAll('p');
for (var i = 0; i < links.length; i++) {
links[i].href=links[i].getAttribute('data-goto');
}
for (var i = 0; i < paras.length; i++) {
paras[i].id=paras[i].getAttribute('data-id');
}
I am trying to mimic the caret of a textarea for the purpose of creating a very light-weight rich-textarea. I don't want to use something like codemirror or any other massive library because I will not use any of their features.
I have a <pre> positioned behind a textarea with a transparent background so i can simulate a highlighting effect in the text. However, I also want to be able to change the font color (so its not always the same). So I tried color: transparent on the textarea which allows me to style the text in any way I want because it only appears on the <pre> element behind the textarea, but the caret disappears.
I have gotten it to work fairly well, although it is not perfect. The main problem is that when you hold down a key and spam that character, the caret seems to always lag one character behind. Not only that, it seems to be quite resource heavy..
If you see any other things in the code that need improvement, feel free to comment on that too!
Here's a fiddle with the code: http://jsfiddle.net/2t5pu/25/
And for you who don't want to visit jsfiddle for whatever reason, here's the entire code:
CSS:
textarea, #fake_area {
position: absolute;
margin: 0;
padding: 0;
height: 400px;
width: 600px;
font-size: 16px;
font: 16px "Courier New", Courier, monospace;
white-space: pre;
top: 0;
left: 0;
resize: none;
outline: 0;
border: 1px solid orange;
overflow: hidden;
word-break: break-word;
padding: 5px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
#fake_area {
/* hide */
opacity: 0;
}
#caret {
width: 1px;
height: 18px;
position: absolute;
background: #f00;
z-index: 100;
}
HTML:
<div id="fake_area"><span></span></div>
<div id="caret"></div>
<textarea id="textarea">test</textarea>
JAVASCRIPT:
var fake_area = document.getElementById("fake_area").firstChild;
var fake_caret = document.getElementById("caret");
var real_area = document.getElementById("textarea");
$("#textarea").on("input keydown keyup propertychange click", function () {
// Fill the clone with textarea content from start to the position of the caret.
// The replace /\n$/ is necessary to get position when cursor is at the beginning of empty new line.
doStuff();
});
var timeout;
function doStuff() {
if(timeout) clearTimeout(timeout);
timeout=setTimeout(function() {
fake_area.innerHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
setCaretXY(fake_area, real_area, fake_caret, getPos("textarea"));
}, 10);
}
function getCaretPosition(el) {
if (el.selectionStart) return el.selectionStart;
else if (document.selection) {
//el.focus();
var r = document.selection.createRange();
if (r == null) return 0;
var re = el.createTextRange(), rc = re.duplicate();
re.moveToBookmark(r.getBookmark());
rc.setEndPoint('EndToStart', re);
return rc.text.length;
}
return 0;
}
function setCaretXY(elem, real_element, caret, offset) {
var rects = elem.getClientRects();
var lastRect = rects[rects.length - 1];
var x = lastRect.left + lastRect.width - offset[0] + document.body.scrollLeft,
y = lastRect.top - real_element.scrollTop - offset[1] + document.body.scrollTop;
caret.style.cssText = "top: " + y + "px; left: " + x + "px";
//console.log(x, y, offset);
}
function getPos(e) {
e = document.getElementById(e);
var x = 0;
var y = 0;
while (e.offsetParent !== null){
x += e.offsetLeft;
y += e.offsetTop;
e = e.offsetParent;
}
return [x, y];
}
Thanks in advance!
Doesn't an editable Div element solve the entire problem?
Code that does the highlighting:
http://jsfiddle.net/masbicudo/XYGgz/3/
var prevText = "";
var isHighlighting = false;
$("#textarea").bind("paste drop keypress input textInput DOMNodeInserted", function (e){
if (!isHighlighting)
{
var currentText = $(this).text();
if (currentText != prevText)
{
doSave();
isHighlighting = true;
$(this).html(currentText
.replace(/\bcolored\b/g, "<font color=\"red\">colored</font>")
.replace(/\bhighlighting\b/g, "<span style=\"background-color: yellow\">highlighting</span>"));
isHighlighting = false;
prevText = currentText;
doRestore();
}
}
});
Unfortunately, this made some editing functions to be lost, like Ctrl + Z... and when pasting text, the caret stays at the beginning of the pasted text.
I have combined code from other answers to produce this code, so please, give them credit.
How do I make an editable DIV look like a text field?
Get a range's start and end offset's relative to its parent container
EDIT: I have discovered something interesting... the native caret appears if you use a contentEditable element, and inside of it you use another element with the invisible font:
<div id="textarea" contenteditable style="color: red"><div style="color: transparent; background-color: transparent;">This is some hidden text.</div></div>
http://jsfiddle.net/masbicudo/qsRdg/4/
The lag is I think due to the keyup triggering the doStuff a bit too late, but the keydown is a bit too soon.
Try this instead of the jQuery event hookup (normally I'd prefer events to polling, but in this case it might give a better feel)...
setInterval(function () { doStuff(); }, 10); // 100 checks per second
function doStuff() {
var newHTML = real_area.value.substring(0, getCaretPosition(real_area)).replace(/\n$/, '\n\u0001');
if (fake_area.innerHTML != newHTML) {
fake_area.innerHTML = newHTML;
setCaretXY(fake_area, real_area, fake_caret, getPos("textarea"));
}
}
...or here for the fiddle:
http://jsfiddle.net/2t5pu/27/
this seems to work great and doesn't use any polls, just like i was talking about in the comments.
var timer=0;
$("#textarea").on("input keydown keyup propertychange click paste cut copy mousedown mouseup change", function () {
clearTimeout(timer);
timer=setTimeout(update, 10);
});
http://jsfiddle.net/2t5pu/29/
maybe i'm missing something, but i think this is pretty solid, and it behaves better than using intervals to create your own events.
EDIT: added a timer to prevent que stacking.
I have this html mark up:
<div>
<figure></figure>
<figure></figure>
<figure></figure>
</div>
and some CSS:
div {
position: relative;
}
figure {
position: absolute;
top: 0; left: 0;
}
Now, what I'm trying to do is push aside so that each element is arranged separately from each other, so when the first has a margin of 0 to the second has a margin of 100px, while second has a margin of 100px third will have a margin of 200px;
and jQuery:
var circle = $('figure'),
f_circle = content_container.find(circle).first(),
n_circle = f_circle.next();
var circle_width = circle.width();
var circle_separate = function(){
n_circle = f_circle;
for(var i=0; i< options.elements_number; i++) {
n_circle.each(function(){
$(this).css({
'margin-left': +circle_width * (options.elements_number -2) + 10 * (options.elements_number - 2) + 'px'
});
})
}
}
And if I have more than 3 elements to behave more similarly, last shied away from before last.
To have this in ouptut:
Thx for help.
;You can use jQuery.each() to cycle through them all, the each functionality gives you the position in the array of the current element and you just need to multiply that by your desired width
var circle = $('figure');
var circle_width = circle.width();
var circle_separate = function(){
circle.each(function(idx){
$(this).css('margin-left',(idx * (circle_width +10))+'px');
})
}
You could do this in numerous ways, you can do it either with CSS or with javascript.
In your code, the figure elements are position: absolute, and margin doesn't matter in absolute elements because they can be placed anywhere within your div using top and left and they don't affect elements before or after them.
Try to write your css like this:
div {
position: relative;
}
figure {
position: relative;
display: inline-block;
}
If you want you can specify individual margins from each other in CSS like this:
figure:first-child { margin-right: 10px; }
figure:nth-child(2) { margin-right: 30px; }
figure:nth-child(3) { margin-right: 50px; }
If you want to make to use jquery to do this and have them as absolute, you can use jquery's each() function
I would do something like this (since you can't use margins in absolute elements that have top/left specified):
var prevLeft = 0;
var prevWidth = 0;
$("figure").each(function(idx, elem){
var $this = $(this);
$this.css({ left: prevLeft+prevWidth+10 });
prevLeft = $this.offset().left;
prevWidth = $this.width();
});