How to target text between two elements - javascript

Update Below
I'm trying to target the output of codemirror and add some custom events and styling to module elements.
Codemirror displays the following code as such.
<div><module type="content"></module><span>can contain other data</span></div>
In the DOM it is rendered between a series of spans.
<pre>
<span class="cm-tag"><div><module</span>
<span class="cm-attribute">type</span>
=
<span class="cm-string">"content"</span>
<span class="cm-tag">></module><span></span>
can contain other data
<span class="cm-tag"></span></div></span>
</pre>
The issue I'm having is trying to add a yellow background to the whole module element, but because the "=" part is between two elements, I'm not sure how to target it with a selector.
This is what I have right now, but because it does not include the text between the elements, there are gaps in the background color.
$('.cm-tag:contains("<module")').each(function () {
var $closingElement;
$(this).nextAll().each(function () {
if ($(this).text() == "></module>") {
$closingElement = $(this).next();
return false;
}
});
var $module =$(this).add($(this).nextUntil($closingElement));
$module.addClass('module');
});
Anyone have suggestion/ideas about how to accomplish this?
Update
I was able to get part way there by using the wrapAll jquery method, but the visible result still isn't quite right. Now the spaces and equal characters are removed from the wrapped element and placed after it.
<modulename"content"id"1234"/> = =
function hilightModules() {
$('.cm-tag:contains("<module")').each(function() {
var $module = $(this);
$(this).nextAll().each(function() {
$module = $module.add($(this));
// closing element
if ($(this).hasClass('cm-tag')) {
return false;
}
});
$module.wrapAll('<span class="module" />').click(function() {
// Do stuff
});
});
};

For adding click handlers to content text, your best bet is to just register a mousedown handler on the CodeMirror wrapper element, and, in that handler, determine whether the thing clicked is what you are looking for. Content elements may change at any time, and you don't want to register tons of handlers.
As for highlighting things, I'd recommend an overlay mode (see http://codemirror.net/demo/mustache.html for an example), rather than trying to do it with DOM munging (for the reasons listed above).

Like #Raminson said, you could target the <pre> tag to make the background span the entire section. Is this what you are looking for?
http://jsfiddle.net/ckaufman/CSzny/

Related

Trying to create new custom HTML elements which perform action in JQuery

I am trying to create my own custom HTML elements where a user can interact with the text within that element. For Example, I created an element where anything between those tags will have a pointer as a mouse cursor and when double clicked, something happens. EG:
<objdc>Double click me!</objdc>
However, this is my code and it is not working:
$(document).ready(function() {
var ObjDblClk = $('objdc');
ObjDblClk.css({ cursor: 'pointer' });
ObjDblClk.dblclick(function(e) {
var range = window.getSelection() || document.getSelection() || document.selection.createRange();
var word = $.trim(range.toString());
if(word != '') {
//Do Something
}
range.collapse();
e.stopPropagation();
});
});
}
Any suggestions?
The problem you have is related with the fact you are not using the collapse method right. It expects a node as parameter and an offset.
So... to fix that exact behavior you posted you would need to do something like:
ObjDblClk.dblclick(function(e) {
var range = window.getSelection() || document.getSelection() || document.selection.createRange();
var word = $.trim(range.toString());
if(word != '') {
//Do Something
}
range.collapse(ObjDblClk[0], 0);
e.stopPropagation();
});
BUT (and this is important): That will do absolutely nothing for your custom selection (especially since is on double click witch affects selection). So you can just remove that line completely and try another solution.
Also: You should read the comments. The guys are right. Unless you are working on some reall strange inhouse thing there may be better aproaches.
Fiddle here (added an alert so you see the function is called - don't forget to select something before double clicking): https://jsfiddle.net/713ndkm0/1/
To create a custom tag like that, you have to be aware of certain things:
Not all browsers will understand your custom tag as a DOM object. IE is a notable example.
Your new custom tag should have a hyphen in it, like obj-dc (more info).
If you want to use it in IE, you have to declare it up-front, as:
document.createElement('obj-dc');
Here is a link to creating new HTML tags for Chrome, in the new way, and here is a link for the older API. As you can see, even the same browser cannot operate with custom tags consistently.

Format text as user inputs in a contenteditable div

I'm attempting to make a page that allows users to input text and it will automatically format the input -- as in a screenplay format (similar to Amazon's StoryWriter).
So far I can check for text with ":contains('example text')" and add/remove classes to it. The problem is that all of the following p tags inherit that class.
My solution so far is to use .next() to remove the class I added, but that is limited since there might be need for a line break in the script (in dialogue for instance) and that will remove the dialogue class.
$('.content').on('input', function() {
$("p.input:contains('INT.')").addClass("high").next(".input").removeClass("high");
$("p.input:contains('EXT.')").addClass("high").next(".input").removeClass("high");
});
I can't get || to work in the :contains parameter either, but that's the least of my issues.
I have a JS fiddle
I've worked on this for a while now, and if I could change only the node that contains the text (INT. or EXT. in this example) and leaves the rest alone that would work and I could apply it to the rest of the script.
Any help would be appreciated, I'm new to the stackoverflow so thank you.
See the comments in the code below for an explanation of what's going on.
Fiddle Example
JQuery
var main = function(){
var content = $('.content');
content.on('input', function() {
$("p.input").each(function() {
//Get the html content for the current p input.
var text = $(this).html();
//indexOf will return a positive value if "INT." or "EXT." exists in the html
if (text.indexOf('INT.') !== -1 || text.indexOf('EXT.') !== -1) {
$(this).addClass('high');
}
//You could include additional "if else" blocks to check and apply different conditions
else { //The required text does not exist, so remove the class for the current input
$(this).removeClass('high');
}
});
});
};//main close
$(document).ready(main);

Add hanging indent to CKEditor on web page [duplicate]

I'm using CKEditor and I want to indent just the first line of the paragraph. What I've done before is click "Source" and edit the <p> style to include text-indent:12.7mm;, but when I click "Source" again to go back to the normal editor, my changes are gone and I have no idea why.
My preference would be to create a custom toolbar button, but I'm not sure how to do so or where to edit so that clicking a custom button would edit the <p> with the style attribute I want it to have.
Depending on which version of CKE you use, your changes most likely disappear because ether the style attribute or the text-indent style is not allowed in the content. This is due to the Allowed Content Filter feature of CKEditor, read more here: http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter
Like Ervald said in the comments, you can also use CSS to do this without adding the code manually - however, your targeting options are limited. Either you have to target all paragraphs or add an id or class property to your paragraph(s) and target that. Or if you use a selector like :first-child you are restricted to always having the first element indented only (which might be what you want, I don't know :D).
To use CSS like that, you have to add the relevant code to contents.css, which is the CSS file used in the Editor contents and also you have to include it wherever you output the Editor contents.
In my opinion the best solution would indeed be making a plugin that places an icon on the toolbar and that button, when clicked, would add or remove a class like "indentMePlease" to the currently active paragraph. Developing said plugin is quite simple and well documented, see the excellent example at http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1 - if you need more info or have questions about that, ask in the comments :)
If you do do that, you again need to add the "indentMePlease" style implementation in contents.css and the output page.
I've got a way to indent the first line without using style, because I'm using iReport to generate automatic reports. Jasper does not understand styles. So I assign by jQuery an onkeydown method to the main iframe of CKEditor 4.6 and I check the TAB and Shift key to do and undo the first line indentation.
// TAB
$(document).ready(function(){
startTab();
});
function startTab() {
setTimeout(function(){
var $iframe_document;
var $iframe;
$iframe_document = $('.cke_wysiwyg_frame').contents();
$iframe = $iframe_document.find('body');
$iframe.keydown(function(e){
event_onkeydown(e);
});
},300);
}
function event_onkeydown(event){
if(event.keyCode===9) { // key tab
event.preventDefault();
setTimeout(function(){
var editor = CKEDITOR.instances['editor1'], //get your CKEDITOR instance here
range = editor.getSelection().getRanges()[0],
startNode = range.startContainer,
element = startNode.$,
parent;
if(element.parentNode.tagName != 'BODY') // If you take an inner element of the paragraph, get the parentNode (P)
parent = element.parentNode;
else // If it takes BODY as parentNode, it updates the inner element
parent = element;
if(event.shiftKey) { // reverse tab
var res = parent.innerHTML.toString().split(' ');
var aux = [];
var count_space = 0;
for(var i=0;i<res.length;i++) {
// console.log(res[i]);
if(res[i] == "")
count_space++;
if(count_space > 8 || res[i] != "") {
if(!count_space > 8)
count_space = 9;
aux.push(res[i]);
}
}
parent.innerHTML = aux.join(' ');
}
else { // tab
var spaces = " ";
parent.innerHTML = spaces + parent.innerHTML;
}
},200);
}
}

How can I create tags using Rangy.js without a class attribute?

I've been playing with Rangy.js for selection ranges and so far really like it. I'm looking to wrap a selection range's text nodes within a certain tag and toggle this upon button click. I have it working great using the cssClassApplierModule with the exception of (and it makes sense due to the name) I HAVE to also give the dom element a class that it's applying to itself.
So right now when I select a range and apply for instance a strong tag, my end result is:
Text text text <strong class="test"> selected text </strong> text text text
And I'd like it to be:
Text text text <strong> selected text </strong> text text text
The code I have so far is as follows:
function gEBI(id) {
return document.getElementById(id);
}
var action;
function toggleAction() {
action.toggleSelection();
}
rangy.init();
// Enable buttons
var cssClassApplierModule = rangy.modules.CssClassApplier;
// Next line is pure paranoia: it will only return false if the browser has no support for ranges,
// selections or TextRanges. Even IE 5 would pass this test.
if (rangy.supported && cssClassApplierModule && cssClassApplierModule.supported) {
action = rangy.createCssClassApplier("test", {
elementTagName: "strong",
elementProperties: { }
});
var toggleActionButton = gEBI(nsID);
toggleActionButton.disabled = false;
toggleActionButton.ontouchstart = toggleActionButton.onmousedown = function () {
toggleAction();
return false;
};
}
I tried "" and null instead of "text" as the css class being passed, and it will toggle, but no longer toggle off and is obviously not the correct solution.
Any help appreciated.. Thanks!
Rangy's CSS class applier won't let you do this, unfortunately. The fundamental problem is that it relies on the CSS class to decide which elements and text nodes to surround or add/remove classes from. It's considerably simpler to detect the presence of a class than the more general case of detecting a style, such as boldness.
I did some work last year on a more ambitious and generic execCommand module that would do what you want. It got as far as a working demo but I got bogged down in tricky edge cases and stopped working on it. I do intend to go back to it but it's likely to be months before anything is ready.

Span tag inside contentEditable div needs keypress event

I have the following div in my html code:
<div id="expanderInput" tabIndex="1" contentEditable></div>
I'm using a contentEditable div as a simple, cross-browser method of making an auto-expanding textbox. I'm also doing input validation on what the user enters into this div. It is supposed to be a list of email addresses separated by commas. When the user tries to submit incorrect information, my Javascript chunks up the input and highlights in red all the incorrect chunks.
//split the address into the comma-separated chunks
var chunks = splitForward(forwardsTo);
//Grab each bad chunk one at a time and stick it in a span with a red-colored text
for(var i = 0; i < invalids.length; i++)
{
var current = chunks[invalids[i]];
current = '<span class="highlighted">' + current + '</span>';
chunks[invalids[i]] = current;
}
$("#expanderInput").html(chunks.join());
initHighlighted();
The array 'invalids' holds the indexes of all the bad chunks. Everything up to this point works fine. But once the user starts typing inside the red text I need the red to disappear, but just in that span. For example, if there are two bad chunks, each highlighted in red, and the user starts fixing one, the other needs to stay red.
I've tried attaching an event listener to the spans:
initHighlighted = function()
{
$(".highlighted").keypress(function()
{
alert("It works!");
});
};
But the event never happens, even when the user edits the text in red. Using browser's developer's tools, I can see that the event handler is there, it is just never set off. Is it the contentEditable attribute on the div causing the span from receiving the event? I've also tried making the spans themselves contentEditable, but the behavior is still the same.
Unless I'm mistaken, this should solve your problem :
initHighlighted = function()
{
$(".highlighted").live("keypress",function()
{
alert("It works!");
});
};
Because your spans are added after the initial loading of the DOM the keypress event listeners haven't been attached as there was nothing to attach them to. Jquery's live sorts this out for you by attaching those listeners to, in this case, anything with the class 'highlighted' no matter when they are added to the DOM.
Read the documentation on the Jquery site to get a much better explanation than I could give you : http://api.jquery.com/live/
EDIT : My apologies for not reading your question properly and realising that your were attaching the keypress listener after after the 'highlighted' spans were added.
Have you read this though :
Keyboard events for child elements in a contenteditable div?

Categories

Resources