Ok, I've tried my best at searching, but.
I've got a task, where I need to load some js using the Ajax and so on. Long story short, I've stuck.
First a code in script.js (which I must load AND which I can't modify):
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
Then my script to load it
$.getScript('script.js', function() {
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log( $('.item').index(this) );
});
});
});
The problem is that on click I need to get index of item in array, i.e. if I click on "Lorem ipsum 4" console should print "4", not "3" as it happens now (because of deleted element which doesn't appear in dom).
Is there a way to get the right result using jQuery?
Ok, I need to say that it's a task. And here is the thing: I simply CAN'T modify script.js. Let's say it's on server and I have no access to it until I get it. But I need index of an element which it has in the original array.
You're asking for the INDEX of the clicked item. Your code is doing exactly what it's supposed to do. jQuery has no way to know if you've deleted items from the original list, it can only see what's currently there.
The best solution is to add an HTML attribute to the original items, and console.log that attribute instead of the .index. Like this:
var divs = [
'<div data-idx="0" class="item">Lorem ipsum 0</div>',
'<div data-idx="1" class="item">Lorem ipsum 1</div>',
'<div data-idx="2" class="item">Lorem ipsum 2</div>',
'<div data-idx="3" class="item">Lorem ipsum 3</div>',
'<div data-idx="4" class="item">Lorem ipsum 4</div>',
'<div data-idx="5" class="item">Lorem ipsum 5</div>',
'<div data-idx="6" class="item">Lorem ipsum 6</div>',
'<div data-idx="7" class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log($(this).data('idx'));
});
});
http://jsfiddle.net/mblase75/8NLGm/
Try something like this:
http://jsfiddle.net/YjNAL/1/
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
delete(divs[3]);
var yep = $('<div class="yep"></div>'); // Changed (from edit)
for (var i = 0; i < divs.length; i++) {
if (divs[i]) { // Don't operate on undefined items
var theDiv = $(divs[i]).data("idx", i); // Changed (from edit)
yep.append(theDiv); // Changed (from edit)
}
}
$(".a").append(yep); // Changed (from edit)
$('.item').on("click", function() {
console.log( $(this).data("idx") );
});
Notice how the original array isn't modified.
Each item in the array is modified and creates a jQuery object before it is appended. <- I'm sure that part could be done more efficiently, I was just trying to throw something together.
It stores its index in the array from of the for loop, so that should be accurate.
Any undefined (deleted) items are ignored.
You can try this. Give all the divs an ID, and get that:
var divs = [
'<div class="item" id="0">Lorem ipsum 0</div>',
'<div class="item" id="1">Lorem ipsum 1</div>',
'<div class="item" id="2">Lorem ipsum 2</div>',
'<div class="item" id="3">Lorem ipsum 3</div>',
'<div class="item" id="4">Lorem ipsum 4</div>',
'<div class="item" id="5">Lorem ipsum 5</div>',
'<div class="item" id="6">Lorem ipsum 6</div>',
'<div class="item" id="7">Lorem ipsum 7</div>'
];
delete(divs[3]);
$('.a').append('<div class="yep">' + divs.join('') + '</div>');
$('.item').each(function() {
$(this).click(function() {
console.log(this.id);
});
});
Numbers in id's?!?
Yea, I know, in HTML4 ids starting with numbers weren't allowed. HTML5, however, removed this restriction.
Those divs will validate according to the w3 validator.
If you would rather have it a bit dynamic, you could create an element, add a data field to it, then access that element:
var divs = [
'<div class="item">Lorem ipsum 0</div>',
'<div class="item">Lorem ipsum 1</div>',
'<div class="item">Lorem ipsum 2</div>',
'<div class="item">Lorem ipsum 3</div>',
'<div class="item">Lorem ipsum 4</div>',
'<div class="item">Lorem ipsum 5</div>',
'<div class="item">Lorem ipsum 6</div>',
'<div class="item">Lorem ipsum 7</div>'
];
var newdivs = $('<div class="yep">').append(divs.join(""));
newdivs.find('.item').each(function() {
$(this).data("myindex", $(this).index());
});
var elementToDelete = 3
delete(divs[elementToDelete]);
newdivs.find('.item').eq(elementToDelete).remove();
$('.a').append(newdivs);
$('.a').on('click','.item',function() {
$(this).css("background-color","lime");
alert($(this).data("myindex"));
});
see how it works here:
http://jsfiddle.net/rZjNd/2/
Related
Hi I've got a string where I want to spilt up the content "ipsum dolar" and wrap it into a span tag and have the background change to red. My code does this but it wraps the two words into separate span tags. How would i amend my code to wrap them into one span tag together? Any help on this would be appreciated.
var findWords = 'ipsum dolor';
var elem = document.querySelectorAll('p.content');
elem.forEach(function(el) {
el.innerHTML = el.textContent.split(' ').map(function(i) {
return findWords.indexOf(i) > -1 ? '<span class="matched">' + i + '</span>' : i;
}).join(' ');
});
.matched {background: red;}
<p class="content"> Lorem ipsum dolor sit amet</p>
<p class="content"> Lorem ipsum dolor sit amet</p>
Using .replace() will be better in this case:
var findWords = 'ipsum dolor';
var elem = document.querySelectorAll('p.content');
elem.forEach(function(el) {
el.innerHTML = el.textContent.replace(new RegExp(findWords, 'g'), '<span class="matched">' + findWords + '</span>');
});
.matched {background: red;}
<p class="content"> Lorem ipsum dolor sit amet</p>
<p class="content"> Lorem ipsum dolor sit amet ipsum dolor</p>
I am making an automatic Table of Content, everything is working fine. I just need to indicated the top heading to be "bold"
jQuery(document).ready(function(){
var ToC =
"<nav role='navigation' class='table-of-contents vNav'>" +
"<ul class='vNav'>";
var newLine, el, title, link;
$(".right_section .intro_title h2, .right_section .section_one_content h2, .right_section .p_section_title h3").each(function() {
el = $(this);
title = el.text();
//link = "#" + el.attr("id");
link = "#" + el.text();
$(this).attr('id', title);
if ($( ".right_section .intro_title h2" )) {
newLine =
"<li class='vNav_heading'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
} else {
newLine =
"<li class='vNav_lists'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
}
ToC += newLine;
});
ToC +=
"</ul>" +
"</nav>";
$(".toc").prepend(ToC);
}); // END DOM
The code above adds a class (.vNav_lists) to all when it should detect (.right_section .intro_title h2) to have a class ".vNav_heading" and the rest should be ".vNav_lists"
This is the HTML markup, this is based on wordpress wysiwyg editor
<div class="left_section">
<div class="toc"></div>
</div>
<div class="right_section">
<div class="intro_title">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="section_one_content">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="p_section_title">
<h3>title</h3>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div>
This is the result, but there should be a class .vNav_heading on every top heading.
<div class="toc">
<nav role="navigation" class="table-of-contents vNav">
<ul class="vNav">
<li class="vNav_lists">class should be .vNav_heading</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">class should be .vNav_heading</li>
<li class="vNav_lists">Text</li>
<li class="vNav_lists">Text</li>
</ul>
</nav>
</div>
Hope its understadable, I could not explain it well
You need to check whether parent has class "intro_title"
$(function(){
var ToC =
"<nav role='navigation' class='table-of-contents vNav'>" +
"<ul class='vNav'>";
var newLine, el, title, link;
$(".right_section .intro_title h2, .right_section .section_one_content h2, .right_section .p_section_title h3").each(function() {
el = $(this);
title = el.text();
//link = "#" + el.attr("id");
link = "#" + el.text();
$(this).attr('id', title);
if ($(this).closest(".intro_title").length) {
newLine =
"<li class='vNav_heading'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
} else {
newLine =
"<li class='vNav_lists'>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
}
ToC += newLine;
});
ToC +=
"</ul>" +
"</nav>";
console.log(ToC);
$(".toc").prepend(ToC);
}); // END DOM
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="toc">
</div>
<div class="right_section">
<div class="intro_title">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="section_one_content">
<h2>title</h2>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
<div class="p_section_title">
<h3>title</h3>
</div>
<p>lorem ipsum dolor</p>
<p>lorem ipsum dolor</p>
</div>
I need to manipulate a string-variable with JavaScript, which has some html-content. I want to search for some elements, change them and wrap these elements with another div-container.
How can I get this:
var myArr = ['Foo', 'Bar'];
var contenthtml = "<p>Foo</p>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem <b>ipsum</b> dolor sit amet</p>
<p>Lorem ipsum dolor sit amet</p>
<p>Bar</p>
<p>Lorem ipsum dolor sit amet</p>";
to this:
contenthtml = "<div class='foo'><h1>Foo</h1>
<p>Lorem ipsum dolor sit amet</p>
<p>Lorem <b>ipsum</b> dolor sit amet</p>
<p>Lorem ipsum dolor sit amet</p></div>
<div class='bar'><h1>Bar</h1>
<p>Lorem ipsum dolor sit amet</p></div>";
You can use a regular expression (similar to my other answer at https://stackoverflow.com/a/21803683/3210837):
var keywordsRegEx = keywords.map(function(x){return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');}).join('|');
var result = str.replace(new RegExp('<p>(' + keywordsRegEx + ')</p>\r\n((?:[ \t]*<p>(?:(?!' + keywordsRegEx + ').)*</p>(?:\r\n)?)*)', 'mgi'), '<div><h1 class="$1">$1</h1>\r\n$2</div>\r\n');
See http://jsfiddle.net/ncu43/1/ for a full example.
What the regular expression does is it matches <p>, one of the keywords, </p>, and then a paragraph (not containing one of the keywords) zero or more times.
I used some DOM to solve this problem. For those who prefer a DOM solution, rather than RegExp:
Append elements in a variable instead of a temporary DOM
This would be a little bit easier with straight-up DOM replacement/wrapping (in fact, I wrote such a solution but re-wrote it when I saw your comment saying you needed string input), but here's a solution using just a string as input:
var myArr = ['Foo', 'Bar'];
var contenthtml = '<p>Foo</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>\n'
+ '<p>Lorem <b>ipsum</b> dolor sit amet</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>\n'
+ '<p>Bar</p>\n'
+ '<p>Lorem ipsum dolor sit amet</p>';
var elements = $.parseHTML(contenthtml);
var tmp = '';
$.each(elements, function(index, element) {
$.each(myArr, function(i, e) {
if (element.innerHTML == e) {
elements[index] = $('<h1>' + e + '</h1>').get(0);
return;
}
});
if (elements[index].outerHTML) {
tmp += elements[index].outerHTML + '\n';
}
});
contenthtml = '<div class="foo">' + tmp + '</div>';
console.log(contenthtml);
jsfiddle
I have the following:
I am trying to set it up so that when you drag the item, it only gets dropped to the div element which you can see, and is not covered up.
So I used this js:
$(".draggable").draggable({
helper: "clone"
})
$("#bottom, .draggable").droppable({
drop: function(event, ui) {
var $this = $(this),
$dragged = $(ui.draggable);
$this.append($dragged.clone());
},
hoverClass: "dragHover"
})
But it drops the element in both places even though only one of the drop zones is not visible!
How do I fix it so that this does not happen?
Fiddle: http://jsfiddle.net/maniator/Wp4LU/
Extra Info to recreate the page without the fiddle:
HTML:
<div id="top">
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
<div class="draggable">
Lorem ipsum dolor sit amet
</div>
</div>
<div id="bottom"></div>
CSS:
.draggable {
border: 1px solid green;
background: white;
padding: 5px;
}
.dragHover{
background: blue;
}
#top {
height: 500px;
overflow-y: scroll;
}
#bottom {
height: 150px;
overflow-y: scroll;
border: red solid 4px;
}
Try setting with accept function.
The working demo.
$("#bottom, .draggable").droppable({
drop: function(event, ui) {
var $this = $(this),
$dragged = $(ui.draggable);
$this.append($dragged.clone());
},
accept: function () {
var $this = $(this), divTop= $("#top");
if ($this.is(".draggable")) {
return $this.offset().top < divTop.offset().top + divTop.height() ;
}
return true;
},
hoverClass: "dragHover"
});
If I got you right - this one should solve your problem - http://jsfiddle.net/Wp4LU/60/
Also you could write custom accept function - http://jqueryui.com/demos/droppable/#option-accept
Code:
var draggableList = $('#top');
$(".draggable").draggable({
helper: "clone"
});
$("#bottom, .draggable").droppable({
drop: function(event, ui) {
var $this = $(this),
$dragged = $(ui.draggable);
if ($this.hasClass("draggable")) {
if ($this.position().top >= draggableList.height() ||
$this.position().top + $this.outerHeight() >=
draggableList.height())
return;
}
$this.append($dragged.clone());
},
hoverClass: "dragHover"
});
According to the sources (jquery.ui.droppable.js), the drop operation will search for every eligible droppable and apply the drop function to every one that intersects with it:
drop: function(draggable, event) {
var dropped = false;
$.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
if(!this.options) return;
if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance))
dropped = this._drop.call(this, event) || dropped;
(Old versions had the last "OR" condition reversed, so it would only apply to a single droppable. Try your fiddle using jQuery 1.5.2 / jQuery UI 1.8.9, and see that it only drops to one element, albeit the "wrong" one...)
And every tolerance mode currently implemented in the $.ui.intersect function only take into account the (x,y) coordinates:
switch (toleranceMode) {
case 'fit':
return (l <= x1 && x2 <= r
&& t <= y1 && y2 <= b);
break;
case 'intersect':
return (l < x1 + (draggable.helperProportions.width / 2) // Right Half
&& x2 - (draggable.helperProportions.width / 2) < r // Left Half
&& t < y1 + (draggable.helperProportions.height / 2) // Bottom Half
&& y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
break;
...
So, unless someone adds a z-index aware tolerance mode, your only option is to work around the issue somehow. I'd suggest first adding every droppable candidate to a set and, when it's time to drop, select only the one that is "closest" to the screen:
$("#bottom, .draggable").droppable({
over: function(event, ui) {
if ( !ui.draggable.data("drop-candidates") )
ui.draggable.data("drop-candidates",[]);
ui.draggable.data("drop-candidates").push(this);
},
out: function(event, ui) {
var that = this,
candidates = ui.draggable.data("drop-candidates") || [];
ui.draggable.data("drop-candidates", $.grep(candidates, function(e) {
return e != that;
});
},
drop: function(event, ui) {
var $this = $(this),
$dragged = $(ui.draggable);
var candidates = $.data("drop-candidates").sort(closestToScreen);
if ( candidates[0] == this )
$this.append($dragged.clone());
},
hoverClass: "dragHover"
})
Now, implementing the closestToScreen comparator is the tricky part. The W3C CSS Specification describes how rendering engines should sort elements to paint, but I wasn't able to find so far an easy way to access this information. I also asked this question here at SO, maybe someone will find a good way.
P.S. If modifying the jQuery UI source is an option, you could try implementing a z-index aware tolerance mode using document.getElementFromPoint, as this answer to said question suggested:
var x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height;
switch (toleranceMode) {
...
case 'z-index-aware':
return document.elementFromPoint(x1,y1) == droppable;
break;
(that would ensure only the element right below the upper-left corner of the draggable would be considered "good enough" as a drop target - not ideal, but better than what we have so far; a similar solution could be adapted to use the mouse pointer coordinates instead)
And, no, you can't use this method with the workaround presented before: at the moment the drop happens, the drag helper is the element closest to the screen... (Edit: d'oh! It wouldn't work if implemented as a tolerance mode either, for the same reason...)
If you just want to drop on element you can see you could change your selector :
$(".draggable:visible").draggable({
helper: "clone"
});
$("#bottom, .draggable:visible").droppable({
drop: function(event, ui) {
var $this = $(this),
$dragged = $(ui.draggable);
$this.append($dragged.clone());
},
hoverClass: "dragHover"
});
Or when you hide an element change its' draggable class by something else.
I have some HTML that comes back from another process looking like this:
Lorem <i style="color:blue;">
<strong>ipsum</strong>
</i>
<i style="color:blue;">
<strong> </strong></i>
<i style="color:blue;">
<strong>test</strong>
</i> dolor sit amet
Note that basically every element (where a word, group of punctuation, or group of whitespace constitutes an "element") has its own set of identical tags wrapped around it. I am trying to find a way in Javascript to simplify it back to this:
Lorem <i style='color:blue;'>
<strong>ipsum test</strong></i>
dolor sit amet
It seems at once both simple and complex. My brain is fried from a full day of power-coding, so I'm not coming up with any creative solutions.
Superthanks!
How about
DEMO
var str = 'Lorem <i style="color:blue;"><strong>ipsum</strong></i><i style="color:blue;"><strong> </strong></i><i style="color:blue;"><strong>test</strong></i> dolor sit amet'
var d = document.createElement('div');
d.innerHTML= str;
var italics = d.getElementsByTagName('i');
var text = str.substring(0,str.indexOf('<i'))
text += '<i style="color:blue;"><strong>';
for (var i=0;i<italics.length;i++) {
text += italics[i].textContent;
}
text += '</strong></i>';
text += str.substring(str.lastIndexOf('>')+1);
console.log(text)
document.getElementById('content').innerHTML=text;
// You have to be really sure of the source:
// (the lines in the string are broken for posting)
var s= 'Lorem <i style="color:blue;"><strong>ipsum</strong></i>'+
'<i style="color:blue;"><strong> </strong></i><i style="color:blue;">'+
'<strong>test</strong></i> dolor sit amet';
s= s.replace(/<\/strong>\s*<\/i>\s*<i[^>]+>\s*<strong>/g, '');
// returned value: (String)
Lorem <i style="color:blue;"><strong>ipsum test</strong></i> dolor sit amet
html:
Lorem ipsum test dolor sit amet