I'm using Mutation Summary library to observe when a particular type of elements have been added and then append my html element to each of them:
var observer = new MutationSummary({
callback: theNextPageLoaded,
queries: [{
element: "li.sweet"
}]
});
function theNextPageLoaded(summaries) {
var sc = summaries[0],
sc_length = sc.added.length,
btn = $('<button>', {
class: 'my_btn',
text: 'Please work!'
});
sc.added.forEach(function(newEl) {
newEl.appendChild(btn);
console.log(typeof(newEl)); //newEl's type is 'object'
});
}
Code above is not working. I'm not sure I can even use appendChild on an object. Any help would be appreciated!
Your problem is mixing together "bare" DOM elements from Mutation Summary response and jQuery elements. Pure DOM appendChild does not understand your jQuery-wrapped btn.
So, you need to make them both to be the same type:
$(newEl).append(btn); // jQuery
newEl.appendChild(btn.get(0)); // pure DOM
Either works, but the first one is probably more idiomatic.
Related
I'm using the Tippy.js v6 library, and I'm struggling to work out how to change the content of any one of my instances using the delegate method. All the answers I've found on SO reference methods that are no longer available in v6.
I'm using v6 via the CDN.
Current delegate code
// Delegate tippy
const delegateInstances = tippy.delegate("#order__lines-table", {
target: "[data-tippy-content]"
});
If I console.log(delegateInstances) this I get an array with my items, but I can't seem to work out how to target 1 single instance inside here.
An example of what I'm doing is when you click a button its state toggles and I want the tippy content to update. I had done this like follows, but they just stack on top of each other, the old content doesn't get removed.
const viewBtnIcon = viewBtn.querySelector('i');
viewBtnIcon.dataset.tippyContent = `View ${data.name}`;
tippy(viewBtnIcon, {
content: `View ${data.name}`
});
Basically I need to know how to target a specific instance and update the content. I have to use the delegate method (from what I understand) as content is dynamically added to the page.
The following code is how I got it to work based on issue #767 and issue #774 on the original GitHub repo
const viewBtnIcon = viewBtn.querySelector('i');
if (viewBtnIcon._tippy) {
viewBtnIcon._tippy.setContent(`View ${data.name}`);
} else {
viewBtnIcon.dataset.tippyContent = `View ${data.name}`;
}
I have a similar use and this is how I change the tip content dynamically:
the content property will be replaced with the content you set onShown, this will work even with async calls.
delegate("#tippyRoot", {
target: "[data-tippy-content]",
content: 'loading',
animation: "scale",
theme: "light",
trigger:'hover',
allowHTML: true,
onShown(instance) {
let cont = instance.reference.dataset.tippyContent;
//do something...
instance.setContent(cont);
},
});
Say I create a javascript class as follows:
class DivClass {
constructor () {
this.div1 = document.createElement('div')
this.div1.id = 'div1'
}
}
Later I instantiate the class as follows:
var divObject = new DivClass()
parentDiv.appendChild(divObject.div1)
and the DIV eventually appears in the DOM.
If I was to locate the 'div1' element within the DOM, say via getElementById() for argument sake, is there anyway of getting back to the javascript 'divObject' responsible for its creation?
From what little I've learned about javascript, I kind of get the impression that the translation from javascript API to DOM is a one way trip and this just simply isn't possible.
Apologies in advance if I've gotten any of the terminology wrong, but I'm kind of new to javascript and still don't fully understand the DOM/API relationship.
Any advice would be greatly appreciated.
You can add a reference to the object to the DIV element.
class DivClass {
constructor () {
this.div1 = document.createElement('div')
this.div1.id = 'div1'
this.div1.divClass = this;
}
}
Then you can use document.getElementById("div1").divClass to get the object.
We are currently using quilljs for a project. When we try to add html through the dangerouslyPasteHTML API from the Clipboard module, the custom attributes from the paragraphs are stripped.
For example:
On applying the following code :
quill.clipboard.dangerouslyPasteHTML("<p data-id='1'>Hello</p>");
The output obtained is
<p>Hello</p>
How do you retain the attribute 'data-id' in the output?
UPDATE 1:
I have managed to retain the custom attribute 'data-id' using the following code:
var Parchment = Quill.import('parchment');
var dataId = new Parchment.Attributor.Attribute('data-id', 'data-id', {
scope: Parchment.Scope.BLOCK
});
Quill.register(dataId);
However, on creating a new line (hitting the enter key), the same data-id is appearing in the new paragraph as well. How do I ensure that the new paragraph either has a custom data-id or does not contain the 'data-id' attribute?
I am pretty late to answer this, but for anyone encountering this issue, I have fixed it in the following way:
import Quill from 'quill';
const Parchment = Quill.import('parchment');
const IDAttribute = new Parchment.Attributor.Attribute('id-attribute', 'id', {
scope: Parchment.Scope.BLOCK,
});
Quill.register(
{
'attributors/attribute/id': IDAttribute,
},
true
);
Quill.register(
{
'formats/id': IDAttribute,
},
true
);
const Block = Quill.import('blots/block');
class BlockBlot extends Block {
constructor(domNode) {
super(domNode);
domNode.setAttribute('id', '');
this.cache = {};
}
}
BlockBlot.blotName = 'block';
export default BlockBlot;
So basically we want to make a custom Blot extending from the Block blot available and use it for the Block format execution. In the constructor we can do whatever we want to do with the attribute. In my case I am removing the id attribute which was being added to new block.
I would recommend adding event handling in the textChanged method. You could check the delta and see if the 'insert' also contains an 'attributes' field that would cause it to be modified. If that happens, you can trigger an updateContents that retains through the current selection index. Then delete the length of the insert, and reinsert without the attributes.
I'm tinkering with writing a more efficient methodology in the creation of dynamically generated DOM elements via JavaScript. This is something I intend to add into my own JS framework later on. Looking for other OOP devs that could help better refine what I do have.
Here's a link to the working CodePen:
http://codepen.io/DaneTheory/pen/yeLvmm/
Here's the JS:
function CreateDOMEl() {};
CreateDOMEl.prototype.uiFrag = document.createDocumentFragment();
CreateDOMEl.prototype.elParent = function(elParent, index) {
this.elParent = document.getElementsByTagName(elParent)[index];
}
CreateDOMEl.prototype.elType = function(type) {
newEl = document.createElement(type);
this.uiFrag.appendChild(newEl);
}
CreateDOMEl.prototype.elContent = function(elContent) {
this.elContent = elContent;
newEl.textContent = elContent;
}
CreateDOMEl.prototype.buildEl = function() {
this.elParent.appendChild(this.uiFrag);
}
var div = new CreateDOMEl();
div.elParent('body', 0);
div.elType('DIV');
div.elContent('OK');
div.buildEl();
console.log(div);
var bttn = new CreateDOMEl();
bttn.elParent('body', 0);
bttn.elType('BUTTON');
bttn.elContent('SUBMIT');
bttn.buildEl();
console.log(bttn);
And some CSS to get elements to appear on page:
div {
width:100px;
height:100px;
border: 1px solid red;
}
My thoughts:
For performance, using the prototype to build methods versus placing all the logic in the constructor.
Rather than directly appending elements to the page, append to a single Document Fragment. Once the element is built out as a Doc Frag, appending the Doc Frag to to the DOM. I like this method for performance, but would like to improve upon it. Any useful implementations of requestnimationFrame, or using range and other versions of the document fragment method?
Silly, but I think for debugging it'd be nice to see the generated Element type within the Object property's on console log. As of right now, console logging a created element will show the elements parent and text content. It'd be great to show the elements type as well.
Creating more than one element at a time is another piece of functionality I'd like to offer as an option. For instance, creating a div element creates one div element. What's a good way to add another optional method to create multiple instances of div's.
div.elType('DIV');
// After calling the elType method, do something like this:
div.elCount(20);
// This would create 20 of the same divs
Lastly, a nice clean way to optionally add attributes (i.e: classes, an ID, value, a placeholder, custom attributes, data-* attributes, etc.). I've got a nice helper function I use that adds multiple attributes to an element in an object literal syntax looking way. Adding this as a method of the constructor would be ideal. Here's that function:
function setAttributes(el, attrs) {
for(var key in attrs) {
el.setAttribute(key, attrs[key]);
}
}
// A use case using the above
// function would be:
var anInputElement = document.createElement("TEXTAREA");
setAttributes(anInputElement, {
"type": "text",
"id": "awesomeID",
"name": "coolName",
"placeholder": "Hey I'm some placeholder example text",
"class": "awesome"
});
// Which creates the following HTML snippet:
<textarea type="text" id="awesomeID" name="coolName" placeholder="Hey I'm some placeholder example text" class="awesome">
As a side note, realizing now that the above helper function needs rewritten so that multiple classes could be created.
Respectfully, I believe you may be overthinking it. Just use the tools available in JavaScript and get 'er done. In terms of performance, computers are so fast at running your JavaScript that you (and me) are unable to perceive, or even comprehend, the speed. Here's how I add a link to an MDL nav menu, for example. It's just vanilla JS. Don't forget to add event listeners.
function navMenuAdd(type,text){
var newAnchor = doc.createElement("anchor");
newAnchor.classList.add('mdl-navigation__link');
newAnchor.classList.add(type);
newAnchor.href = "javascript:void(0)";
var anchorContent = doc.createTextNode(text);
newAnchor.appendChild(anchorContent);
newAnchor.addEventListener('click', navMenuClickHandler, false);
//newAnchor.style.display = 'none';
if (type === 'Thingy A'){
//insertAfter(newAnchor, navMenuCredentials);
navMenuCredentialsPanel.appendChild(newAnchor);
} else if (type === 'Thingy B'){
//insertAfter(newAnchor, navMenuDevices);
navMenuDevicesPanel.appendChild(newAnchor);
}
}
I have a DOM element like this:
<div id='master-value'>Apples</div>
I have many other elements elsewhere on the page that I need to sync with the 'master-value'.
<div class='fruit-value' data-reference='master-value'>Apples</div>
<div class='some-fruit' data-reference='master-value'>Apples</div>
When I change the value of the 'master-value', I want all the synced elements to update with it.
$('#master-value').text('Pears');
Should affect:
<div class='fruit-value' data-reference='master-value'>Pears</div>
<div class='some-fruit' data-reference='master-value'>Pears</div>
What I don't want, is on every change of 'master-value' to have to search through all the elements in order to find the synced elements in order to change them. I think that's quite slow when there are many elements that needs to be searched through.
There should be some way for the child values to be pre-bound to the master value so that the selection goes quickly.
$('.fruit-value, .some-fruit').sync('#master-value');
I have some ideas, for instance: I can create an array of preselected synced objects, bind a custom event on the master value and run that event whenever I change the value. The event would go through the array to update all the child elements.
I'm sure there's a better way of doing it though...
Thanks!
You can store the selector once, like this:
var elements = $('.fruit-value, .some-fruit'); //do this once
elements.text($("#master-value").text()); //when you want to sync
The elements variable/jQuery object will keep an array of references to DOM elements so it won't be traversing to find them each time.
wouldn't it be easier to give them all the same class?
So you coud do
$('.fruit').text('Pears')
If you're looking for plugin type of functionality, try this:
When setting up, it takes an object with one property syncWith to set up the elements it should sync with.
When setting the text, it will set the text for the master and the synced elements.
Try it out: http://jsfiddle.net/GH33J/
Just a first attempt. There would be room for improvement if (for example) the master was more than one element. There should be a global reference to all the elements to synchronize and an option to tell if the masters should be synced too.
$.fn.sync = function(arg) {
// if arg plain object, we are doing an initial setup
if ($.isPlainObject(arg)) {
return this.each(function() {
$.data(this, 'syncWith', $(arg.syncWith));
});
// if arg is jQuery object, we are adding new items
} else if (arg.jquery) {
return this.each(function() {
var $set = $.data(this, 'syncWith');
$.each(arg, function() {
$set.push(this);
});
});
console.log(this.data('syncWith'));
// otherwise assume we have a string, and are syncing a new value
} else {
return this.each(function() {
$(this).text(arg);
$.data(this, 'syncWith').text(arg);
});
}
};
// Set up the sync
$('#master-value').sync({
syncWith: '.fruit-value,.some-fruit'
});
var $new = $('<div class="fruit-value">Apples</div>').appendTo('body');
// Pass a jQuery object containing newly created element(s) to add to the set
$('#master-value').sync($new);
// Activate a sync
$('#master-value').sync("pears");
OK here we go:
This is the official data linking plugin from Microsoft. It's now being supported by the jQuery Core team, so we know it's good. :)
http://weblogs.asp.net/scottgu/archive/2010/05/07/jquery-templates-and-data-linking-and-microsoft-contributing-to-jquery.aspx
http://blog.jquery.com/2010/10/04/new-official-jquery-plugins-provide-templating-data-linking-and-globalization/