Get caret HTML position in contenteditable DIV - javascript

I am having troubles figuring out how to get caret position in a DIV container that contains HTML tags.
I am using this JavaScript function to do that:
function getCaretPosition()
{
if (window.getSelection && window.getSelection().getRangeAt)
{
var range = window.getSelection().getRangeAt(0);
var selectedObj = window.getSelection();
var rangeCount = 0;
var childNodes = selectedObj.anchorNode.parentNode.childNodes;
for (var i = 0; i < childNodes.length; i++)
{
if (childNodes[i] == selectedObj.anchorNode)
{
break;
}
if(childNodes[i].outerHTML)
{
rangeCount += childNodes[i].outerHTML.length;
}
else if(childNodes[i].nodeType == 3)
{
rangeCount += childNodes[i].textContent.length;
}
}
return range.startOffset + rangeCount;
}
return -1;
}
However, it finds a caret position of the text in my DIV container, when I need to find the caret position including HTML tags.
For example:
<DIV class="peCont" contenteditable="true">Text goes here along with <b>some <i>HTML</i> tags</b>.</DIV>;
(please note, that HTML tags are normal tags and are not displayed on the screen when the function is returning caret position)
If I click right between H and TML, the aforementioned function will find caret position without any problems. But I am getting the contents of DIV box in HTML format (including all tags), and if I want to insert something at that caret's position, I will be off by a few or many characters.
I went through many posts, but all I could find is either <TEXTAREA> caret postions, or functions similar to what I have posted. So far I still cannot find a solution to get a caret position in a text that has HTML formatting.
Can anyone help, please?
PS. Here's JQuery/Javascript code that I wrote for the link button:
$('#pageEditor').on('click', '.linkURL', function()
{
var cursorPosition;
cursorPosition = getCaretPosition();
var contentID = $(this).parent().parent().attr('id');
var userSelected = getSelectionHtml();
var checkLink = userSelected.search('</a>');
var anchorTag = 0;
if(checkLink == -1)
{
var currentContents = $('#'+contentID+' .peCont').html();
var indexOfSelection = currentContents.indexOf(userSelected);
var getPossibleAnchor = currentContents.slice(indexOfSelection, indexOfSelection+userSelected.length+6);
anchorTag = getPossibleAnchor.search('</a>');
}
if(checkLink > 0 || anchorTag > 0)
{
//alert(checkLink);
document.execCommand('unlink', false, false);
}
else
{
$('#'+contentID+' .peCont').append('<div id="linkEntry"><label for="urlLink">Please enter URL for the link:<label><input type="text" id="urlLink" /></div>');
$('#linkEntry').dialog({
buttons: {
"Ok": function()
{
var attribute = $('#urlLink').val();
var newContentWithLink = '';
if(attribute != '')
{
if(userSelected != '')
{
var currentContent = $('#'+contentID+' .peCont').html();
var replacement = ''+userSelected+'';
newContentWithLink = currentContent.replace(userSelected, replacement);
}
else
{
var currentTextContent = $('#'+contentID+' .peCont').html();
var userLink = ''+attribute+'';
if(cursorPosition > 0)
{
var contentBegin = currentTextContent.slice(0,cursorPosition);
var contentEnd = currentTextContent.slice(cursorPosition,currentTextContent.length);
newContentWithLink = contentBegin+userLink+contentEnd;
}
else
{
newContentWithLink = attribute+currentTextContent;
}
}
$('#'+contentID+' .peCont').empty();
$('#'+contentID+' .peCont').html(newContentWithLink);
}
$(this).dialog("close");
} },
closeOnEscape:true,
modal:true,
resizable:false,
show: { effect: 'drop', direction: "up" },
hide: { effect: 'drop', direction: "down" },
width:460,
closeText:'hide',
close: function()
{
$(this).remove();
}
});
$('#linkEntry').on('keypress', function(urlEnter)
{
if(urlEnter.which == 13)
{
var attribute = $('#urlLink').val();
var newContentWithLink = '';
if(userSelected != '')
{
var currentContent = $('#'+contentID+' .peCont').html();
var replacement = ''+userSelected+'';
newContentWithLink = currentContent.replace(userSelected, replacement);
}
else
{
var currentTextContent = $('#'+contentID+' .peCont').html();
var userLink = ''+attribute+'';
if(cursorPosition > 0)
{
var contentBegin = currentTextContent.slice(0,cursorPosition);
var contentEnd = currentTextContent.slice(cursorPosition,currentTextContent.length);
newContentWithLink = contentBegin+userLink+contentEnd;
}
else
{
newContentWithLink = attribute+currentTextContent;
}
}
$('#'+contentID+' .peCont').empty();
$('#'+contentID+' .peCont').html(newContentWithLink);
$(this).dialog("close");
}
});
}
});

I created a simple fiddle for you that does what you want.
This should work in recent versions of Firefox, Chrome and Safari.
It's your homework to add Opera and IE support if you need.

Related

JavaScript - Set function to return all items based on 1 or 2 selected tags (NO jQUERY)

I have some JavaScrip that is meant to check if there are any media tags selected or industry tags selected--this is so the portfolio items can be sorted and displayed accordingly in the browser.
What I have almost works 100%, but I can't figure out how to make it so that if only a media tag is selected or if only an industry tag is selected, the portfolio items should still be sorted accordingly. Currently, you have to select a media tag AND an industry tag, but I'd like users to be able to search using just a media tag OR just an industry tag.
Here is what I want to accomplish: If only a media tag is selected, then get all portfolio pieces that are associated with that media tag. If only an industry tag is selected, get all portfolio items that are associated with that industry tag. If a media tag AND industry tag are selected at the same time, get all portfolio items that are associated with BOTH.
Vanilla JS isn't my strong point so forgive me if this is a dumb question, but this has had me stumped for hours now.
No jQuery answers, please, as this whole page's functionality is built using JavaScript.
Here is the function:
var update = function () {
closeDrawer();
// update ui to reflect tag changes
// get our list of items to display
var itemsToDisplay = [];
var currentMediaTag = controlsContainer.querySelector('.media.selected');
var currentIndustryTag = controlsContainer.querySelector('.industry.selected');
if (currentMediaTag != "" && currentMediaTag != null) {
selectedMediaFilter = currentMediaTag.innerHTML;
}
if (currentIndustryTag != "" && currentIndustryTag != null) {
selectedIndustryFilter = currentIndustryTag.innerHTML;
}
if (selectedMediaFilter == "" && selectedIndustryFilter == "") {
itemsToDisplay = portfolioItems.filter(function (item) {
return item.preferred;
});
} else {
itemsToDisplay = portfolioItems.filter(function (item) {
var mediaTags = item.media_tags,
industryTags = item.industry_tags;
if(industryTags.indexOf(selectedIndustryFilter) < 0){
return false;
}
else if(mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
else{
return true;
}
});
}
renderItems(itemsToDisplay);
}
Not entirely sure it's necessary but just in case, here is the complete JS file that handles the portfolio page:
(function ($) {
document.addEventListener("DOMContentLoaded", function (event) {
// for portfolio interaction
var portfolioGrid = (function () {
var gridSize = undefined,
parentContainer = document.querySelector('.portfolio-item-container');
containers = parentContainer.querySelectorAll('.view'),
drawer = parentContainer.querySelector('.drawer'),
bannerContainer = drawer.querySelector('.banner-container'),
thumbsContainer = drawer.querySelector('.thumbs-container'),
descriptionContainer = drawer.querySelector('.client-description'),
clientNameContainer = drawer.querySelector('.client-name'),
controlsContainer = document.querySelector('.portfolio-controls-container'),
selectedMediaFilter = "", selectedIndustryFilter = "";
var setGridSize = function () {
var windowSize = window.innerWidth,
previousGridSize = gridSize;
if (windowSize > 1800) {
gridSize = 5;
} else if (windowSize > 900) {
gridSize = 4;
} else if (windowSize > 600 && windowSize <= 900) {
gridSize = 3;
} else {
gridSize = 2;
}
if (previousGridSize != gridSize) {
closeDrawer();
}
};
var attachResize = function () {
window.onresize = function () {
setGridSize();
};
};
var getRowClicked = function (boxNumber) {
return Math.ceil(boxNumber / gridSize);
};
var getLeftSibling = function (row) {
var cI = row * gridSize;
return containers[cI >= containers.length ? containers.length - 1 : cI];
};
var openDrawer = function () {
drawer.className = 'drawer';
scrollToBanner();
};
var scrollToBanner = function () {
var mainContainer = document.querySelector('#main-container'),
mainBounding = mainContainer.getBoundingClientRect(),
scrollY = (drawer.offsetTop - mainBounding.bottom) - 10,
currentTop = document.body.getBoundingClientRect().top;
animate(document.body, "scrollTop", "", document.body.scrollTop, scrollY, 200, true);
};
var animate = function (elem, style, unit, from, to, time, prop) {
if (!elem) return;
var start = new Date().getTime(),
timer = setInterval(function () {
var step = Math.min(1, (new Date().getTime() - start) / time);
if (prop) {
elem[style] = (from + step * (to - from)) + unit;
} else {
elem.style[style] = (from + step * (to - from)) + unit;
}
if (step == 1) clearInterval(timer);
}, 25);
elem.style[style] = from + unit;
}
var closeDrawer = function () {
drawer.className = 'drawer hidden';
};
var cleanDrawer = function () {
bannerContainer.innerHTML = "";
clientNameContainer.innerHTML = "";
descriptionContainer.innerHTML = "";
thumbsContainer.innerHTML = "";
};
var resetThumbs = function () {
Array.prototype.forEach.call(thumbsContainer.querySelectorAll('.thumb'), function (t) {
t.className = "thumb";
});
};
var handleBannerItem = function (item) {
bannerContainer.innerHTML = "";
if (item.youtube) {
var videoContainer = document.createElement('div'),
iframe = document.createElement('iframe');
videoContainer.className = "videowrapper";
iframe.className = "youtube-video";
iframe.src = "https://youtube.com/embed/" + item.youtube;
videoContainer.appendChild(iframe);
bannerContainer.appendChild(videoContainer);
} else if (item.soundcloud) {
var iframe = document.createElement('iframe');
iframe.src = item.soundcloud;
iframe.className = "soundcloud-embed";
bannerContainer.appendChild(iframe);
} else if (item.banner) {
var bannerImage = document.createElement('img');
bannerImage.src = item.banner;
bannerContainer.appendChild(bannerImage);
}
};
var attachClick = function () {
Array.prototype.forEach.call(containers, function (n, i) {
n.querySelector('a.info').addEventListener('click', function (e) {
e.preventDefault();
});
n.addEventListener('click', function (e) {
var boxNumber = i + 1,
row = getRowClicked(boxNumber);
var containerIndex = row * gridSize;
if (containerIndex >= containers.length) {
// we're inserting drawer at the end
parentContainer.appendChild(drawer);
} else {
// we're inserting drawer in the middle somewhere
var leftSiblingNode = getLeftSibling(row);
leftSiblingNode.parentNode.insertBefore(drawer, leftSiblingNode);
}
// populate
cleanDrawer();
var mediaFilterSelected = document.querySelector('.media-tags .tag-container .selected');
var selectedFilters = "";
if (mediaFilterSelected != "" && mediaFilterSelected != null) {
selectedFilters = mediaFilterSelected.innerHTML;
}
var portfolioItemName = '';
var selectedID = this.getAttribute('data-portfolio-item-id');
var data = portfolioItems.filter(function (item) {
portfolioItemName = item.name;
return item.id === selectedID;
})[0];
clientNameContainer.innerHTML = data.name;
descriptionContainer.innerHTML = data.description;
var childItems = data.child_items;
//We will group the child items by media tag and target the unique instance from each group to get the right main banner
Array.prototype.groupBy = function (prop) {
return this.reduce(function (groups, item) {
var val = item[prop];
groups[val] = groups[val] || [];
groups[val].push(item);
return groups;
}, {});
}
var byTag = childItems.groupBy('media_tags');
if (childItems.length > 0) {
handleBannerItem(childItems[0]);
var byTagValues = Object.values(byTag);
byTagValues.forEach(function (tagValue) {
for (var t = 0; t < tagValue.length; t++) {
if (tagValue[t].media_tags == selectedFilters) {
handleBannerItem(tagValue[0]);
}
}
});
childItems.forEach(function (item, i) {
var img = document.createElement('img'),
container = document.createElement('div'),
label = document.createElement('p');
container.appendChild(img);
var mediaTags = item.media_tags;
container.className = "thumb";
label.className = "childLabelInactive thumbLbl";
thumbsContainer.appendChild(container);
if (selectedFilters.length > 0 && mediaTags.length > 0) {
for (var x = 0; x < mediaTags.length; x++) {
if (mediaTags[x] == selectedFilters) {
container.className = "thumb active";
label.className = "childLabel thumbLbl";
}
}
}
else {
container.className = i == 0 ? "thumb active" : "thumb";
}
img.src = item.thumb;
if (item.media_tags != 0 && item.media_tags != null) {
childMediaTags = item.media_tags;
childMediaTags.forEach(function (cMTag) {
varLabelTxt = document.createTextNode(cMTag);
container.appendChild(label);
label.appendChild(varLabelTxt);
});
}
img.addEventListener('click', function (e) {
scrollToBanner();
resetThumbs();
handleBannerItem(item);
container.className = "thumb active";
});
});
}
openDrawer();
});
});
};
var preloadImages = function () {
portfolioItems.forEach(function (item) {
var childItems = item.child_items;
childItems.forEach(function (child) {
(new Image()).src = child.banner;
(new Image()).src = child.thumb;
});
});
};
//////////////////////////////////// UPDATE FUNCTION /////////////////////////////////////
var update = function () {
closeDrawer();
// update ui to reflect tag changes
// get our list of items to display
var itemsToDisplay = [];
var currentMediaTag = controlsContainer.querySelector('.media.selected');
var currentIndustryTag = controlsContainer.querySelector('.industry.selected');
if (currentMediaTag != "" && currentMediaTag != null) {
selectedMediaFilter = currentMediaTag.innerHTML;
}
if (currentIndustryTag != "" && currentIndustryTag != null) {
selectedIndustryFilter = currentIndustryTag.innerHTML;
}
if (selectedMediaFilter == "" && selectedIndustryFilter == "") {
itemsToDisplay = portfolioItems.filter(function (item) {
return item.preferred;
});
} else {
itemsToDisplay = portfolioItems.filter(function (item) {
var mediaTags = item.media_tags,
industryTags = item.industry_tags;
if (industryTags.indexOf(selectedIndustryFilter) < 0) {
return false;
}
else if (mediaTags.indexOf(selectedMediaFilter) < 0) {
return false;
}
else {
return true;
}
});
}
renderItems(itemsToDisplay);
}
//////////////////////////////////// RENDERITEMS FUNCTION /////////////////////////////////////
var renderItems = function (items) {
var children = parentContainer.querySelectorAll('.view');
Array.prototype.forEach.call(children, function (child) {
// remove all event listeners then remove child
parentContainer.removeChild(child);
});
items.forEach(function (item) {
var container = document.createElement('div'),
thumb = document.createElement('img'),
mask = document.createElement('div'),
title = document.createElement('h6'),
excerpt = document.createElement('p'),
link = document.createElement('a');
container.className = "view view-tenth";
container.setAttribute('data-portfolio-item-id', item.id);
thumb.src = item.thumb;
mask.className = "mask";
title.innerHTML = item.name;
excerpt.innerHTML = item.excerpt;
link.href = "#";
link.className = "info";
link.innerHTML = "View Work";
container.appendChild(thumb);
container.appendChild(mask);
mask.appendChild(title);
mask.appendChild(excerpt);
mask.appendChild(link);
parentContainer.insertBefore(container, drawer);
});
containers = parentContainer.querySelectorAll('.view');
attachClick();
};
var filterHandler = function (linkNode, tagType) {
var prevSelection = document.querySelector("." + tagType + '.selected');
if (prevSelection != "" && prevSelection != null) {
prevSelection.className = tagType + ' tag';
}
linkNode.className = tagType + ' tag selected';
update();
};
var clearFilters = function (nodeList, filterType) {
Array.prototype.forEach.call(nodeList, function (node) {
node.className = filterType + " tag";
console.log("Clear filters function called");
});
}
var attachFilters = function () {
var mediaFilters = controlsContainer.querySelectorAll('.tag.media'),
industryFilters = controlsContainer.querySelectorAll('.tag.industry'),
filterToggle = controlsContainer.querySelectorAll('.filter-toggle');
// resets
controlsContainer.querySelector('.media-tags .reset')
.addEventListener('click',
function (e) {
e.preventDefault();
selectedMediaFilter = "";
clearFilters(controlsContainer.querySelectorAll('.media-tags a.tag'), "media");
update();
}
);
controlsContainer.querySelector('.industry-tags .reset')
.addEventListener('click',
function (e) {
e.preventDefault();
selectedIndustryFilter = "";
clearFilters(controlsContainer.querySelectorAll('.industry-tags a.tag'), "industry");
update();
}
);
Array.prototype.forEach.call(filterToggle, function (toggle) {
toggle.addEventListener('click', function (e) {
if (controlsContainer.className.indexOf('open') < 0) {
controlsContainer.className += ' open';
} else {
controlsContainer.className = controlsContainer.className.replace('open', '');
}
});
});
//Attaches a click event to each media tag "button"
Array.prototype.forEach.call(mediaFilters, function (filter) {
filter.addEventListener('click', function (e) {
e.preventDefault();
// var selectedMediaFilter = controlsContainer.querySelector('.media.selected');
//console.log("Media tag: " +this.innerHTML); *THIS WORKS*
filterHandler(this, "media");
});
});
Array.prototype.forEach.call(industryFilters, function (filter) {
filter.addEventListener('click', function (e) {
e.preventDefault();
// var selectedIndustryFilter = this.querySelector('.industry.selected');
// console.log("Industry tag: " +this.innerHTML); *THIS WORKS*
filterHandler(this, "industry");
});
});
};
return {
init: function () {
setGridSize();
attachResize();
attachClick();
preloadImages();
// portfolio page
if (controlsContainer) {
attachFilters();
}
}
};
})();
portfolioGrid.init();
});
}());
$ = jQuery.noConflict();
if(industryTags.indexOf(selectedIndustryFilter) < 0){
return false;
}
else if(mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
That part is giving you headaches. Whenever no industry tag or media tag is selected this will exit the function.
Change to:
if(industryTags.indexOf(selectedIndustryFilter) < 0 && mediaTags.indexOf(selectedMediaFilter) < 0){
return false;
}
Now it will test if at least one tag is selected. If so then render items.
I made a change just to experiment with an idea, and this setup works:
if((selectedIndustryFilter !="" && industryTags.indexOf(selectedIndustryFilter) < 0) || (selectedMediaFilter !="" && mediaTags.indexOf(selectedMediaFilter) < 0)){
return false;
}
return true;
Not sure if it's the best solution ever but it seems to work and I'm not going to complain.

Need help changing menu from hover to click

--- Original Hover code ---
function bind_dropdown() {
$('div#quick_nav div.search_dropdown').on('hover', function() {
var thisid = $(this).attr('id').split('_');
var this_action = thisid[1];
var this_width = 950;
var add_class = 'dropdown_full';
if(this_action == 4) {
this_width = 200;
add_class = 'dropdown_small';
}
if($('div#quick_nav div#dropdown_'+escape(this_action)).is(':visible')) {
$('div#quick_nav div#dropdown_'+escape(this_action)).addClass(add_class).slideUp(100);
}
else{
$('div#quick_nav div.dropdown').hide();
$('div#quick_nav div#dropdown_'+escape(this_action)).addClass(add_class).slideDown(100).width(this_width);
}
return false;
});
}
--- End Original Code ---
I did try and change hover to click. It works however to close the menu you now have to click on the menu icon again. Any help would be greatly appreciated. I would like to be able to click to show menu and when the mouse moves off the menu it would close automatically.
Thank you in advance
J
Use the below code
function bind_dropdown() {
$('div#quick_nav div.search_dropdown').on('click', function() {
var thisid = $(this).attr('id').split('_');
var this_action = thisid[1];
var this_width = 950;
var add_class = 'dropdown_full';
if(this_action == 4) {
this_width = 200;
add_class = 'dropdown_small';
}
if($('div#quick_nav div#dropdown_'+escape(this_action)).is(':visible')) {
$('div#quick_nav div#dropdown_'+escape(this_action)).addClass(add_class).slideUp(100);
}
return false;
}).on("mouseleave", function() {
var thisid = $(this).attr('id').split('_');
var this_action = thisid[1];
var this_width = 950;
var add_class = 'dropdown_full';
if(this_action == 4) {
this_width = 200;
add_class = 'dropdown_small';
}
$('div#quick_nav div.dropdown').hide();
$('div#quick_nav div#dropdown_'+escape(this_action)).addClass(add_class).slideDown(100).width(this_width);
});
}

Jquery : swap two value and change style

i need to make a script for select a black div by click(go red), and put black div value into a white div value by another click, this is ok but when i try to swap values of two white case, the change do correctly one time, but if i retry to swap two value of white case the values swap correctly but whitout the background color red.
This is my code :
var lastClicked = '';
var lastClicked2 = '';
$(".blackcase").click(function(e) {
var i = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.blackcase').removeClass('red');
if (lastClicked != this.id) {
$(this).addClass('red');
var currentId = $(this).attr('id');
var currentVal = $(this).html();
$(".whitecase").click(function(e) {
$('.blackcase').removeClass('red');
var currentId2 = $(this).attr('id');
if (i <= 0 && $("#" + currentId2).html().length == 0) {
$("#" + currentId2).html(currentVal);
$("#" + currentId).html("");
i = 1;
}
});
} else {
lastClicked = this.id;
}
}
});
$(".whitecase").click(function(e) {
var j = 0;
if ($(this).html().length == 0) {
return false;
} else {
e.preventDefault();
$('.whitecase').removeClass('red');
if (lastClicked2 != this.id) {
$(this).addClass('red');
var currentId0 = $(this).attr('id');
var currentVal0 = $(this).html();
$(".whitecase").click(function(e) {
e.preventDefault();
var currentId02 = $(this).attr('id');
var currentVal02 = $(this).html();
if (j <= 0 && currentVal0 != currentVal02) {
$('.whitecase').removeClass('red');
$("#" + currentId02).html(currentVal0);
$("#" + currentId0).html(currentVal02);
j = 1;
return false;
}
});
} else {
lastClicked2 = this.id;
}
}
});
This is JSfiddle :
https://jsfiddle.net/12gwq95u/12/
Try to take 12 and put into first white case, put 39 into second white case, click on the white case with 12 (go red) then click on the white case with 39, the values swap correctly with the red color when it's select, but if you try to reswap two whitecase values thats work but without the red color.
Thanks a lot
I have spent some time to rewrite your code to make it more clear. I don't know what exactly your code should do but according to the information you have already provided, my version of your code is the following:
var selectedCase = {color: "", id: ""};
function removeSelectionWithRed() {
$('div').removeClass('red');
}
function selectWithRed(element) {
removeSelectionWithRed();
element.addClass('red');
}
function updateSelectedCase(color, id) {
selectedCase.color = color;
selectedCase.id = id;
}
function moveValueFromTo(elemFrom, elemTo) {
elemTo.html(elemFrom.html());
setValueToElem("", elemFrom);
}
function setValueToElem(value, elem) {
elem.html(value);
}
function swapValuesFromTo(elemFrom, elemTo) {
var fromValue = elemFrom.html();
var toValue = elemTo.html();
setValueToElem(fromValue, elemTo);
setValueToElem(toValue, elemFrom);
}
function isSelected(color) {
return selectedCase.color == color;
}
function clearSelectedCase() {
selectedCase.color = "";
selectedCase.id = "";
}
function elemIsEmpty(elem) {
return elem.html().length == 0;
}
$(".blackcase").click(function (e) {
if (elemIsEmpty($(this))) {
return;
}
alert("black is selected");
selectWithRed($(this));
updateSelectedCase("black", $(this).attr("id"), $(this).html());
});
$(".whitecase").click(function (e) {
removeSelectionWithRed();
if (isSelected("black")) {
alert("moving black to white");
moveValueFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
if(isSelected("white") && selectedCase.id !== $(this).attr("id")) {
alert("swap whitecase values");
swapValuesFromTo($("#"+selectedCase.id), $(this));
clearSelectedCase();
return;
}
alert("white is selected");
selectWithRed($(this));
updateSelectedCase("white", $(this).attr("id"), $(this).html());
});
Link to jsfiddle: https://jsfiddle.net/12gwq95u/21/
If my answers were helpful, please up them.
It happens because you have multiple $(".whitecase").click() handlers and they don't override each other but instead they all execute in the order in which they were bound.
I advise you to debug your code in browser console by setting breakpoints in every click() event you have (in browser console you can find your file by navigating to the Sources tab and then (index) file in the first folder in fiddle.jshell.net).
In general I think you should rewrite you code in such a way that you won't have multiple handlers to the same events and you can be absolutely sure what your code does.

How to use getSelection with an iframe

I have been trying use the getSelection command to select highlighted text from an iframe and insert it into a variable. Using the follow code I could make this work a textarea.
<script type="text/javascript">
function GetSelectedText () {
var selText = "";
if (window.getSelection) {
if (document.activeElement &&
(document.activeElement.tagName.toLowerCase () == "textarea" ))
{
var text = document.activeElement.value;
selText = text.substring (document.activeElement.selectionStart,
document.activeElement.selectionEnd);
}
else {
var selRange = window.getSelection ();
selText = selRange.toString ();
}
}
else {
if (document.selection.createRange) {
var range = document.selection.createRange ();
selText = range.text;
}
}
var highlight = selText;
if (highlight !== "") {
alert (highlight);
}
if (highlight == "") {
alert ("No string selected");
}
} </script>
<body >
<br /><br />
<textarea onclick="GetSelectedText ()">Some text in a textarea element.</textarea>
</body>
But when I add the following line of code, it still won't execute in an iframe.
|| document.activeElement.tagName.toLowerCase () == "iframe"
How can I fix this so I can highlight a string from my iframe and have the script execute?
UPDATED:
<script type="text/javascript">
var editorDoc;
function InitEditable () {
var editor = document.getElementById ("editor");
if (editor.contentDocument)
editorDoc = editor.contentDocument;
else
editorDoc = editor.contentWindow.document;
var editorBody = editorDoc.body;
if ('contentEditable' in editorBody) {
// allow contentEditable
editorBody.contentEditable = true;
}
else { // Firefox earlier than version 3
if ('designMode' in editorDoc) {
// turn on designMode
editorDoc.designMode = "on";
}
}
}
var iframe = document.getElementById('editor');
var innerDoc = iframe.contentDocument;
function GetSelectedText () {
var selText = "";
if (innerDoc.getSelection) {
if (innerDoc.activeElement &&
(innerDoc.activeElement.tagName.toLowerCase () == "iframe"))
{
var text = innerDoc.activeElement.value;
selText = text.substring (innerDoc.activeElement.selectionStart,
innerDoc.activeElement.selectionEnd);
}
else {
var selRange = innerDoc.getSelection ();
selText = selRange.toString ();
}
}
else if
(innerDoc.selection.createRange) {
var range = innerDoc.selection.createRange ();
selText = range.text;
}
var highlight = selText;
if (highlight !== "") {
alert (highlight);
}
if (highlight == "") {
alert ("No string selected");
}
}
</script>
<body onload="InitEditable ();">
<iframe onclick="GetSelectedText ()" id="editor"></iframe>
</body>
It sounds like the script runs in the iframe's parent window. As such, you will need to use a different object than document to access it:
var iframe = document.getElementById('iframeId');
var innerDoc = iframe.contentDocument;

JQuery placeholder HTML5 simulator

I have been using the HTML 5 placeholder and just realised that it does not work outside HTML5 devices. As you can see by the code below the placeholder is always in lowercase and the value is always in upper case.
#maphead input::-webkit-input-placeholder {
text-transform:lowercase;
}
#maphead input:-moz-placeholder {
text-transform:lowercase;
}
<input id="start" type="text" spellcheck="false" placeholder="enter your post code" style="text-transform:uppercase;" class="capital"/>
This is all fine except when dealing with non HTML 5 devices. For this I have employed a bastardised bit of javascript.
function activatePlaceholders() {
var detect = navigator.userAgent.toLowerCase();
if (detect.indexOf("safari") > 0) return false;
var inputs = document.getElementsByTagName("input");
for (var i=0;i<inputs.length;i++) {
if (inputs[i].getAttribute("type") == "text") {
var placeholder = inputs[i].getAttribute("placeholder");
if (placeholder.length > 0 || value == placeholder) {
inputs[i].value = placeholder;
inputs[i].onclick = function() {
if (this.value == this.getAttribute("placeholder")) {
this.value = "";
}
return false;
}
inputs[i].onblur = function() {
if (this.value.length < 1) {
this.value = this.getAttribute("placeholder");
$('.capital').each(function() {
var current = $(this).val();
var place = $(this).attr('placeholder');
if (current == place) {
$(this).css('text-transform','lowercase')
}
});
}
}
}
}
}
}
window.onload = function() {
activatePlaceholders();
}
Firstly this Javascript is rancid. There must be an easier JQuery way. Now although this above does work (reasonably) it does not respond to keeping the placeholder in lowercase and the value in uppercase since it sets the value with the placeholder.
I've set you all up with a nice Fiddle http://jsfiddle.net/Z9YLZ/1/
Try something like this:
$(function() {
$('input[type="text"]').each(function () {
$(this).focus(function () {
if ($(this).attr('value') === $(this).attr('placeholder')) {
$(this).css('text-transform','lowercase');
$(this).attr('value', '');
}
}).blur(function () {
if ($(this).attr('value') === '') {
$(this).css('text-transform','uppercase');
$(this).attr('value', $(this).attr('placeholder'));
}
}).blur();
});
});
Edit: Explicitly declare the text-transform to cascade properly.
Try this one, I'm using it for a while and it works perfectly:
(function($, undefined) {
var input = document.createElement('input');
if ('placeholder' in input) {
$.fn.hinttext = $.hinttext = $.noop;
$.hinttext.defaults = {};
delete input;
return;
}
delete input;
var boundTo = {},
expando = +new Date + Math.random() * 100000 << 1,
prefix = 'ht_',
dataName = 'hinttext';
$.fn.hinttext = function(options) {
if (options == 'refresh') {
return $(this).each(function() {
if ($(this).data(dataName) != null) {
focusout.call(this);
}
});
}
options = $.extend({}, $.hinttext.defaults, options);
if (!(options.inputClass in boundTo)) {
$('.' + options.inputClass)
.live('focusin click', function() {
$($(this).data(dataName)).hide();
})
.live('focusout', focusout);
boundTo[options.inputClass] = true;
}
return $(this).each(function(){
var input = $(this),
placeholder = input.attr('placeholder');
if (placeholder && input.data(dataName) === undefined) {
var input_id = input.attr('id'),
label_id = prefix + expando++;
if (!input_id) {
input.attr('id', input_id = prefix + expando++);
}
$('<label/>')
.hide()
.css('position', options.labelPosition)
.addClass(options.labelClass)
.text(placeholder)
.attr('for', input_id)
.attr('id', label_id)
.insertAfter(input);
input
.data(dataName, '#' + label_id)
.addClass(options.inputClass)
.change(function() {
focusout.call(this);
});
}
focusout.call(this);
});
};
$.hinttext = function(selector, options) {
if (typeof selector != 'string') {
options = selector;
selector = 'input[placeholder],textarea[placeholder]';
}
$(selector).hinttext(options);
return $;
};
$.hinttext.defaults = {
labelClass: 'placeholder',
inputClass: 'placeholder',
labelPosition: 'absolute'
};
function focusout() {
var input = $(this),
pos = input.position();
$(input.data(dataName)).css({
left: pos.left + 'px',
top: pos.top + 'px',
width: input.width() + 'px',
height: input.height() + 'px'
})
.text(input.attr('placeholder'))
[['show', 'hide'][!!input.val().length * 1]]();
}
$($.hinttext);
})(jQuery);
You just need to make sure to style label.placeholder with CSS to look the same as HTML5 placeholder text (color: #999)
Try this: http://jsfiddle.net/msm595/Z9YLZ/12/
Bit late but here's what I do. Store all the default values on page load and then clear value text ONLY when it's the default value that is clicked on. This prevents the JS clearing user entered text.
jQuery(document).ready(function($){
var x = 0; // count for array length
$("input.placeholder").each(function(){
x++; //incrementing array length
});
var _values = new Array(x); //create array to hold default values
x = 0; // reset counter to loop through array
$("input.placeholder").each(function(){ // for each input element
x++;
var default_value = $(this).val(); // get default value.
_values[x] = default_value; // create new array item with default value
});
var current_value; // create global current_value variable
$('input.placeholder').focus(function(){
current_value = $(this).val(); // set current value
var is_default = _values.indexOf(current_value); // is current value is also default value
if(is_default > -1){ //i.e false
$(this).val(''); // clear value
}
});
$('input.placeholder').focusout(function(){
if( $(this).val() == ''){ //if it is empty...
$(this).val(current_value); //re populate with global current value
}
});
});

Categories

Resources