I have the following elements:
<div contenteditable="true" id="write">
<div class="tooltip">
<span>test</span> <!--text to be written in text area-->
<span class="tooltiptext"> <!--holds words to be displayed on hover-->
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">dog</span>
</span>
</div>
<div class="tooltip">
<span>test</span>
<span class="tooltiptext">
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">test</span>
</span>
</div>
</div>
These basically show a pop-up similar to the following - http://www.w3schools.com/css/tryit.asp?filename=trycss_tooltip
On hover event of elements having the '.tooltop' class (words to be displayed inside the pop-up area), I would like to swap the word which is hovered in the pop-up area, with the word displayed in the text area after a couple of seconds.
I did the following function:
//--choosing word from pop-up list--//
$(document).on("mouseover", ".popUpWord", function(e)
{
if(!timeoutId)
{
timeoutId=window.setTimeout(function()
{
timeoutId=null;
e.currentTarget.innerHTML = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerText;
/*not working*/ e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerHTML = e.currentTarget.innerHTML; //Although the elements I want to swap are referred to correctly,the element's text is not changing. (Tried using innerText)
},1500);
}
}).on('mouseout', '.popUpWord', function(e)
{
if(timeoutId)
{
window.clearTimeout(timeoutId);
timeoutId=null;
}
});
However, in the line marked not working - the element's text is not changing. And it is being referred to correctly.
Any help is greatly appreciated.
Thanks
Its because you are assigning the value of tooltip to the text element and re-assigning it to the tooltip.
Try this:
timeoutId=null;
var text = e.currentTarget.innerHTML;
e.currentTarget.innerHTML = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerText;
e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0].innerHTML = text;
You are trying to locate the source and destination elements in a way that can only lead to brittleness and errors. Give elements you know you want to work with ids and reference them that way.
Try this out. Hover over "helo".
var orig = $("orig");
var hold = $("hold");
var timeoutId = null;
$(".popUpWord").on("mouseover", function(e){
if(!timeoutId) {
timeoutId = setTimeout(function() {
e.target.innerHTML = original.textContent;
original.innerHTML = e.target.innerHTML;
},1500);
}
}).on('mouseout', function(e){
if(timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable="true" id="write">
<div class="tooltip">
<span id="original">test</span> <!--text to be written in text area-->
<span class="tooltiptext"> <!--holds words to be displayed on hover-->
<span id="hold" class="popUpWord">hello</span>
<br>
<span class="popUpWord">dog</span>
</span>
</div>
<div class="tooltip">
<span>test</span>
<span class="tooltiptext">
<span class="popUpWord">hello</span>
<br>
<span class="popUpWord">test</span>
</span>
</div>
</div>
You will need to store the new text strings in a variable. Otherwise you are just setting it right back:
timeoutId=window.setTimeout(function()
{
var child = e.currentTarget,
parent = e.fromElement.parentElement.parentElement.childNodes[0].childNodes[0],
childtext = child.innerText,
parenttext = parent.innerText
child.innerHTML = parenttext;
parent.innerHTML = childtext;
},1500);
Related
I'm building a wee letter-writing tool that will have a range of paragraphs the user can choose to add to a letter, but I worry that I'm doing it in a really inefficient way.
Currently, it's structured like this:
<p id="deposit-dispute" class="paragraph">This is a paragraph about deposits not being protected</p>
<button onclick="addPara(depositDispute)" class="add">Add paragraph</button>
and then in the Javascript, I create a const that pulls the inner HTML of that id:
const depositDispute = "\n" + document.getElementById("deposit-dispute").innerHTML + "\n";
which the addPara() function then adds to the textarea:
function addPara(text) {
document.getElementById("text-body").value += text;
}
But would there be a way to make the function just call whatever the previous p element had in it, rather than having to give them all unique IDs and creating a unique variable for them all?
Here it is in a codepen so you can see what I'm trying to do - the paragraphs to be added are in the accordion on the right: https://codepen.io/gordonmaloney/pen/GRWyjOP
Thanks a lot - and big apologies if this is a ridiculously amateurish question, I've spent ages trying to google a solution but can't find a thing!
G
Each box contains a paragraph and a button.
We can get all the boxes and each box paragraph and button, and finally add click event to the button to insert the paragraph html of this box to the textarea
// Get textarea and boxes
var textarea = document.getElementById('textarea');
var boxes = document.querySelectorAll('.box');
// get the button and the paragraph of each box
boxes.forEach(box => {
var btn = box.querySelector('.button');
var paragraph = box.querySelector('.paragraph');
// add the html of the selected box paragraph to the textarea
btn.addEventListener('click', () => {
textarea.value += "\n" + paragraph.innerHTML; + "\n";
});
});
<div class="wrapper">
<div class="box">
<p class="paragraph">This is paragraph 1</p>
<button class="button">Add to textarea</button>
</div>
<div class="box">
<p class="paragraph">This is paragraph 2</p>
<button class="button">Add to textarea</button>
</div>
<div class="box">
<p class="paragraph">This is paragraph 3</p>
<button class="button">Add to textarea</button>
</div>
</div>
<textarea name="" id="textarea" cols="30" rows="10"></textarea>
I am creating a news feed with VueJS and I have run into a bit of a problem with rendering the content. The API I am using sadly I am unable to change to suit my need properly at this time. The API gives me all the content already in HTML tags and it can also include images and lists and all the other basics. What I want to do is create a "read more" section which will render the first 20 words if just the text of the first "p" tag and stop there.
Does anyone know a quick and efficient way of doing this with JS?
My current display VueJS render is the following:
<div v-for="news_item in news_items">
<div v-bind:class="{ 'col-md-4': display}">
<div class="card">
<div class="header">
<h2>
{{news_item.title}} <small>{{news_item.subtitle}}</small>
</h2>
</div>
<div class="body" style="padding-top: 0">
<div class="row" style="margin-right: -20px; margin-left: -20px;">
<div class="col-md-12"
style="padding-left: 0px; padding-right: 0px;">
<img :src="news_item['thumbnail']"
class="img-responsive smaller-img" alt=""
style=" margin: 0 auto; max-height: 250px;">
</div>
</div>
<div class="row">
<div class="col-md-12">
<div v-html="news_item.content"></div>
</div>
</div>
</div>
</div>
</div>
</div>
This is the perfect time to use a directive:
https://v2.vuejs.org/v2/guide/custom-directive.html
See the codepen here: https://codepen.io/huntleth/pen/GOXaLo
Using the trim directive, you can change the content of the element. In the example above, it will show the first 5 words followed by an ellipsis.
If you're just after a pure js solution, this should do it:
var resultString = str.split(' ').slice(0, 20).join(" ");
You could use the trim directive and search the el for any p tags, and then change their content accordingly.
You don't appear to have tried anything yet, so I'll just give you these pointers. If you run into specific problems, ask again.
Make a component
The component should receive the html as a prop
The component should have a data item to control whether it is expanded
The component should have a computed that gets the first 20 words of the first paragraph tag. You can use textContent to get text from an HTML node.
The computed is the most likely part to pose a challenge. It will look something like this
blurb() {
const div = document.createElement('div');
div.innerHTML = this.content; // this.content is the prop
const firstP = div.querySelector('p');
const text = firstP.textContent;
const match = text.match(/(\S+\s*){0,20}/);
return match[0];
}
Rough implementation, Pure Js approach
document.getElementById("addContent").onclick = display;
document.getElementById("ellipsAnchor").onclick = hideEllipsis;
function display() {
document.getElementById("instruction").classList+= " hide";
let content = document.getElementById("inputbox").value;
if(content.length > 30) {
let sliced = content.slice(30);
let unsliced = content.substring(0,29);
let spantag = document.createElement("span");
spantag.className = "toReplace hide"
let text = document.createTextNode(sliced);
spantag.appendChild(text);
let spantag1 = document.createElement("span");
let text1 = document.createTextNode(unsliced);
spantag1.appendChild(text1);
let contentTag =document.getElementById("content");
contentTag.appendChild(spantag1)
contentTag.appendChild(spantag)
document.getElementById("ellipsis").classList -= "hide";
}
}
function hideEllipsis(){
document.getElementById("ellipsis").classList += " hide";
document.querySelectorAll("span.hide")[0].classList -= " hide"
}
.hide {
display : none;
}
<textarea type="text" id="inputbox"></textarea>
<button id="addContent">
Show content
</button>
<div id="content">
</div>
<div class="hide" id="ellipsis">
Read More..
</div>
<div id="instruction">
Type more than 30 characters and click show content
</div>
You can write a vue directive to solve this.
Set max-height to the div.
count the words and append "Read more.." link to the content.
Add a click event to 'read more' to expand the DIV to full height.
For example see this codepen
let handler = ""
Vue.directive("viewmore", {
inserted: function (el, binding){
let maxlines = binding.value
let lineheight = parseFloat(getComputedStyle(el).lineHeight)
let paddingtop = parseFloat(getComputedStyle(el).paddingTop)
let lines = (el.clientHeight) / lineheight ;
let maxheight = (lineheight * maxlines) + paddingtop + (lineheight/5)
if(lines>maxlines){
el.classList.add('vmore')
el.style.maxHeight = maxheight + 'px'
el.addEventListener('click', handler = ()=> {
el.style.maxHeight = ""
el.scrollIntoView({behavior: "smooth"})
el.removeEventListener('click', handler)
el.classList.remove('vmore')
})
}
},
unbind: function (el, binding) {
el.removeEventListener('click', handler)
handler = ""
}
});
https://codepen.io/dagalti/pen/vPOZaB .
it works based on the lines in the content.
Code : https://gist.github.com/dagalti/c8fc86cb791a51fe24e5dc647507c4a3
Expanding on the answers by tom_h and Roy J, here's what I'm using in my vue application to make the ellipsis clickable:
Vue.component("ellipsis", {
template: "#ellipsis-template",
props: ['content'],
data: function() {
return {
wordLength: 3, // default number of words to truncate
showAll: false
}
}
});
<script type="text/x-template" id="ellipsis-template">
<span v-if="content.split(' ').length>wordLength && showAll">{{content}}
(less)
</span>
<span v-else-if="content.split(' ').length>wordLength && !showAll">
{{content.split(" ").slice(0,wordLength).join(" ")}}
...
</span>
<span v-else>{{content}}</span>
</script>
To call it:
<ellipsis :content="someData"></ellipsis>
I want to update a container with a new version without replacing it. For example:
Container1:
<div id="container-one">
<p>
<webview src="http://i.stack.imgur.com/Ed1aw.jpg"></webview>
</p>
<p>
Text
</p>
</div>
Container2:
<div id="container-two">
<p>
Cool intro
<webview src="http://i.stack.imgur.com/Ed1aw.jpg"></webview>
</p>
<p>
Long text
</p>
<p>
New Paragraph with text in it.
</p>
</div>
Container1 updated:
<div id="container-one">
<p>
Cool intro
<webview src="http://i.stack.imgur.com/Ed1aw.jpg"></webview>
</p>
<p>
Long text
</p>
<p>
New Paragraph with text in it.
</p>
</div>
A Container1.innerHTML = Container2.innerHTMLwould be simple but I don't want to reload my webview so the code should detect new divs or updated content in existing divs and apply the modifications in Container1.
UPDATE :
Container2 is a new version of container1 edited by a user, so Container2 can have anything in it: images, links, new paragraphs.
How can I do this?
I might have not understood your question correctly, but by adding an id to the text that you want to replace, and using simple javascript, you can achieve this.
HTML
<div id="container-one">
<p>
<span id="inner-one-1"></span>
<webview src="http://i.stack.imgur.com/Ed1aw.jpg"></webview>
</p>
<p>
<span id="inner-one-2">Text</span>
</p>
</div>
<div id="container-two">
<p>
<span id="inner-two-1">Cool intro</span>
<webview src="http://i.stack.imgur.com/Ed1aw.jpg"></webview>
</p>
<p>
<span id="inner-two-2">Long text</span>
</p>
</div>
<button id="click">Click Me!</button>
JS
document.getElementById("click").onclick = function() {
document.getElementById("inner-one-2").innerHTML = document.getElementById("inner-two-2").innerHTML;
document.getElementById("inner-one-1").innerHTML = document.getElementById("inner-two-1").innerHTML;
}
DEMO HERE
please try it.
var container_one = $("#container-one").children();
var container_two = $("#container-two").children();
$.each(container_one, function(index, element){
var cont_one_html = $(this).html();
var cont_two_html = $(container_two[index]).html();
if(cont_one_html != cont_two_html){
$(this).html(cont_two_html);
}
});
please have a look on image for more understanding.
you need to get the text node and replace the content, like
$('button').on('click', function () {
// this block is for not to update the webview
// get the first text node
var txt = $('#container-one > p:first').contents().first().get(0);
if (txt.nodeType === 3) { // Node.TEXT_NODE
txt.nodeValue = $('#container-two > p:first').text();
}
// update rest of the elements
$('#container-two').children().each(function (i) {
if (i !== 0 && $('#container-one').children()[i]) {
$($('#container-one').children()[i]).html($(this).html());
}
if (!$('#container-one').children()[i]) {
$('#container-one').append($(this).clone());
}
});
});
DEMO
Have a try and see if this works for you.
var first = $('container-one p:first');
first.html($('container-two p:first').html() + first.html());
$('container-one p:last').html($('container-two p:last').html());
Use this:
function update(s){
document.getElementById("container-one").innerHTML=s.innerHTML;
}
setInterval(function(){
update(document.getElementById("container-two"));
},1000);
What this does is it updates the content once every second.
To show that it can handle dynamic content, I have made the second div editable.
Demo:http://jsfiddle.net/5RLV2/2
I need a script to remove a line of text "Quantity in Stock:(Out of Stock)" from my product pages. The specific HTML code I'm trying to remove is:
<span class="PageText_L329n"><strong class="prod_qty_label">
Quantity in Stock
</strong></span>
:
<span style="color:#cc0000;"><span class="StockQuantity_OutOfStock"></span></span><br></br>
I only have limited knowledge of Javascript, and I know I need to start with something like this:
document.body.innerHTML = document.body.innerHTML.replace('', '');
But I'm struggling to make it work with having all the HTML markups in the javascript.
On my category pages (different page), I am trying to remove this text from the HTML, which may appear multiple times in the page:
<b><font color="#CC0000">
<span class="PageText_L331n">(Out of Stock)</span>
</font></b>
Any help is appreciated.
Thanks.
Like this example?
<div>
<span class="PageText_L328n"><strong class="prod_qty_label">
Quantity in Stock 1
</strong></span>
</div>
<div>
<span class="PageText_L329n"><strong class="prod_qty_label">
Quantity in Stock 2
</strong></span>
</div>
<div>
<span class="PageText_L330n"><strong class="prod_qty_label">
Quantity in Stock 3
</strong></span>
</div>
<button id="button">Remove</button>
var button = document.getElementById("button");
var PageText_L329n = document.getElementsByClassName("PageText_L329n");
function remove() {
if (PageText_L329n[0]) {
PageText_L329n[0].parentNode.removeChild(PageText_L329n[0]);
}
}
button.addEventListener("click", remove, false);
on jsfiddle
Also, maybe it is enough just to hide it rather than remove it from the DOM.
And for clearing text
<b><font color="#CC0000">
<span class="PageText_L331n">(Out of Stock)</span>
</font></b>
<button id="clear">Clear</button>
var button = document.getElementById("clear");
function clearText() {
document.getElementsByClassName("PageText_L331n")[0].textContent = "";
}
button.addEventListener("click", clearText, false);
on jsfiddle
In html I am having the following tags:
<span id=M26>2011-2012</span>
<div id=c26 STYLE="display:none">
<span id=M27>2012-2013</span>
<div id=c26 STYLE="display:none">
On Clicking on 2011-2012 or on 2012-2013 I want to set display property of div tag.
I am using the following Javascript code for this and I am calling the Javascript function in body tag. The output is showing style and display is not an object or property.
<script language="javascript">
function clickHnadler()
{
var xid= document.getElementsByTagName("span");
var xsp= xid[0].id;
alert("Span id is "+xsp);
if(xsp.charAt(0)=="M")
{
var oC = document.all("C"& xsp.substring(1,2));
if(oC.STYLE.display == "none")
{
oC.Style.Display = "";
}
else{
oC.Style.Display = "none";
}
}
}
</script>
use jquery:
you can pass in the function the element or the Id:
ex:
<span id=M26>2011-2012</span>
function clickHnadler(element)
{
var id = $(element > span).attr(id);
id[0] = 'c'; //not the nicest way, maybe use a replace or something like that
$(id).show(); //or $(id).css('display','list');
}
You may use clickHandler has following way,
function clickHandler(e) {
window.document.links[0].handleEvent(e);
}
You need to bind event spacifically to elements you want to handle click for. for more information please refer following link,
http://docs.oracle.com/cd/E19957-01/816-6409-10/evnt.htm#1009606
Based on what i understand from your question, I come up with this.
<script type="text/javascript" src="jquery1.8.js"></script>
<span id=M26>2011-2012</span>
<div id=c26 STYLE="display:none">
2011-2012 details</div>
<br />
<span id=M27>2012-2013</span>
<div id=c26 STYLE="display:none">
2012-2013 details
</div>