Creating and updating link to instagram image or video

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












I've been using the InstagramPhotoLink extension for Opera for some time but instagram broke it recently. I thought I could try to fix it... and I did.



The original extension is rather limited and it supports only a link to the first image and no videos so while fixing it I completely rewrote it in order to extend it.



It works by looking for an image or video element with a specific class and extracts the src attribute from it and puts it in a new a-element inside the media-container.



Switching images/videos is supported by the MutationObserver that updates the links when either a div or an attribute changes.



I came across a problem where the MutatorObserver reacts too quickly and before the src attribute of the new image is set. I solved it by adding a small delay by adding a call to setTimeout to wait until everything is updated.



function createMediaLink() 

function addOrUpdateImageLink(mo)

let image = document.getElementsByClassName('FFVAD');

if (image.length == 0)
return false;


image = image[0];

if (mo)
// the nearest div to the image that stays there when switching images
var div = document.getElementsByClassName("rQDP3");
mo.observe(div[0],
childList: true
);


setTimeout(() =>
addOrUpdateMediaLink(image.src);
, 50);

return true;


function addOrUpdateVideoLink(mo)

let video = document.getElementsByClassName('tWeCl');

if (video.length == 0)
return false;


video = video[0];

if (mo)
mo.observe(video,
attributes: true
);


setTimeout(() =>
addOrUpdateMediaLink(video.src);
, 50);

return true;


function addOrUpdateMediaLink(src)
console.log(`src: $src`);
let a = document.getElementsByClassName('_open_');
if (a.length == 0)
a = document.createElement("a");
a.className = "_open_";
a.innerHTML = 'Open in new tab';
a.target = "_blank";
// media container
document.getElementsByClassName("_97aPb")[0].appendChild(a);
else
a = a[0];

a.href = src;


createMediaLink();


I don't write javascript too often so this is the best I can do but I'm pretty sure this can still be improved. What do you think?







share|improve this question





















  • Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
    – Sam Onela
    Jun 11 at 4:20










  • @SamOnela this works only in single item view, not lists.
    – t3chb0t
    Jun 11 at 4:23










  • Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
    – Sam Onela
    Jun 11 at 16:01











  • @SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
    – t3chb0t
    Jun 11 at 16:12

















up vote
3
down vote

favorite












I've been using the InstagramPhotoLink extension for Opera for some time but instagram broke it recently. I thought I could try to fix it... and I did.



The original extension is rather limited and it supports only a link to the first image and no videos so while fixing it I completely rewrote it in order to extend it.



It works by looking for an image or video element with a specific class and extracts the src attribute from it and puts it in a new a-element inside the media-container.



Switching images/videos is supported by the MutationObserver that updates the links when either a div or an attribute changes.



I came across a problem where the MutatorObserver reacts too quickly and before the src attribute of the new image is set. I solved it by adding a small delay by adding a call to setTimeout to wait until everything is updated.



function createMediaLink() 

function addOrUpdateImageLink(mo)

let image = document.getElementsByClassName('FFVAD');

if (image.length == 0)
return false;


image = image[0];

if (mo)
// the nearest div to the image that stays there when switching images
var div = document.getElementsByClassName("rQDP3");
mo.observe(div[0],
childList: true
);


setTimeout(() =>
addOrUpdateMediaLink(image.src);
, 50);

return true;


function addOrUpdateVideoLink(mo)

let video = document.getElementsByClassName('tWeCl');

if (video.length == 0)
return false;


video = video[0];

if (mo)
mo.observe(video,
attributes: true
);


setTimeout(() =>
addOrUpdateMediaLink(video.src);
, 50);

return true;


function addOrUpdateMediaLink(src)
console.log(`src: $src`);
let a = document.getElementsByClassName('_open_');
if (a.length == 0)
a = document.createElement("a");
a.className = "_open_";
a.innerHTML = 'Open in new tab';
a.target = "_blank";
// media container
document.getElementsByClassName("_97aPb")[0].appendChild(a);
else
a = a[0];

a.href = src;


createMediaLink();


I don't write javascript too often so this is the best I can do but I'm pretty sure this can still be improved. What do you think?







share|improve this question





















  • Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
    – Sam Onela
    Jun 11 at 4:20










  • @SamOnela this works only in single item view, not lists.
    – t3chb0t
    Jun 11 at 4:23










  • Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
    – Sam Onela
    Jun 11 at 16:01











  • @SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
    – t3chb0t
    Jun 11 at 16:12













up vote
3
down vote

favorite









up vote
3
down vote

favorite











I've been using the InstagramPhotoLink extension for Opera for some time but instagram broke it recently. I thought I could try to fix it... and I did.



The original extension is rather limited and it supports only a link to the first image and no videos so while fixing it I completely rewrote it in order to extend it.



It works by looking for an image or video element with a specific class and extracts the src attribute from it and puts it in a new a-element inside the media-container.



Switching images/videos is supported by the MutationObserver that updates the links when either a div or an attribute changes.



I came across a problem where the MutatorObserver reacts too quickly and before the src attribute of the new image is set. I solved it by adding a small delay by adding a call to setTimeout to wait until everything is updated.



function createMediaLink() 

function addOrUpdateImageLink(mo)

let image = document.getElementsByClassName('FFVAD');

if (image.length == 0)
return false;


image = image[0];

if (mo)
// the nearest div to the image that stays there when switching images
var div = document.getElementsByClassName("rQDP3");
mo.observe(div[0],
childList: true
);


setTimeout(() =>
addOrUpdateMediaLink(image.src);
, 50);

return true;


function addOrUpdateVideoLink(mo)

let video = document.getElementsByClassName('tWeCl');

if (video.length == 0)
return false;


video = video[0];

if (mo)
mo.observe(video,
attributes: true
);


setTimeout(() =>
addOrUpdateMediaLink(video.src);
, 50);

return true;


function addOrUpdateMediaLink(src)
console.log(`src: $src`);
let a = document.getElementsByClassName('_open_');
if (a.length == 0)
a = document.createElement("a");
a.className = "_open_";
a.innerHTML = 'Open in new tab';
a.target = "_blank";
// media container
document.getElementsByClassName("_97aPb")[0].appendChild(a);
else
a = a[0];

a.href = src;


createMediaLink();


I don't write javascript too often so this is the best I can do but I'm pretty sure this can still be improved. What do you think?







share|improve this question













I've been using the InstagramPhotoLink extension for Opera for some time but instagram broke it recently. I thought I could try to fix it... and I did.



The original extension is rather limited and it supports only a link to the first image and no videos so while fixing it I completely rewrote it in order to extend it.



It works by looking for an image or video element with a specific class and extracts the src attribute from it and puts it in a new a-element inside the media-container.



Switching images/videos is supported by the MutationObserver that updates the links when either a div or an attribute changes.



I came across a problem where the MutatorObserver reacts too quickly and before the src attribute of the new image is set. I solved it by adding a small delay by adding a call to setTimeout to wait until everything is updated.



function createMediaLink() 

function addOrUpdateImageLink(mo)

let image = document.getElementsByClassName('FFVAD');

if (image.length == 0)
return false;


image = image[0];

if (mo)
// the nearest div to the image that stays there when switching images
var div = document.getElementsByClassName("rQDP3");
mo.observe(div[0],
childList: true
);


setTimeout(() =>
addOrUpdateMediaLink(image.src);
, 50);

return true;


function addOrUpdateVideoLink(mo)

let video = document.getElementsByClassName('tWeCl');

if (video.length == 0)
return false;


video = video[0];

if (mo)
mo.observe(video,
attributes: true
);


setTimeout(() =>
addOrUpdateMediaLink(video.src);
, 50);

return true;


function addOrUpdateMediaLink(src)
console.log(`src: $src`);
let a = document.getElementsByClassName('_open_');
if (a.length == 0)
a = document.createElement("a");
a.className = "_open_";
a.innerHTML = 'Open in new tab';
a.target = "_blank";
// media container
document.getElementsByClassName("_97aPb")[0].appendChild(a);
else
a = a[0];

a.href = src;


createMediaLink();


I don't write javascript too often so this is the best I can do but I'm pretty sure this can still be improved. What do you think?









share|improve this question












share|improve this question




share|improve this question








edited Jun 11 at 17:06









Sam Onela

5,76961543




5,76961543









asked Jun 9 at 15:21









t3chb0t

31.9k54195




31.9k54195











  • Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
    – Sam Onela
    Jun 11 at 4:20










  • @SamOnela this works only in single item view, not lists.
    – t3chb0t
    Jun 11 at 4:23










  • Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
    – Sam Onela
    Jun 11 at 16:01











  • @SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
    – t3chb0t
    Jun 11 at 16:12

















  • Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
    – Sam Onela
    Jun 11 at 4:20










  • @SamOnela this works only in single item view, not lists.
    – t3chb0t
    Jun 11 at 4:23










  • Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
    – Sam Onela
    Jun 11 at 16:01











  • @SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
    – t3chb0t
    Jun 11 at 16:12
















Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
– Sam Onela
Jun 11 at 4:20




Do you use this when viewing a list of images/videos and/or when viewing a single item (and navigating through a collection - e.g. using left/right clicks/arrow key presses)?
– Sam Onela
Jun 11 at 4:20












@SamOnela this works only in single item view, not lists.
– t3chb0t
Jun 11 at 4:23




@SamOnela this works only in single item view, not lists.
– t3chb0t
Jun 11 at 4:23












Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
– Sam Onela
Jun 11 at 16:01





Okay - I am trying to see the line var div = document.getElementsByClassName("rQDP3"); working... if I go to the album view cats_of_instagram I don't see any results there, then if I click on an item, it opens the modal but I still don't see any results there... if I refresh the item is displayed but not in a modal. When do you see results for that?
– Sam Onela
Jun 11 at 16:01













@SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
– t3chb0t
Jun 11 at 16:12





@SamOnela try this class name _97aPb - this seems to be a more common one that works on any page. I see that the one I used in my question applies only to certain pages :-|
– t3chb0t
Jun 11 at 16:12











2 Answers
2






active

oldest

votes

















up vote
4
down vote



accepted











  • addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;

  • In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;

  • Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;

  • Use strict equality operator (===) wherever possible — it performs no type conversion;

  • Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.

Rewrite



const addOrUpdateMediaLink = src => 
if (!src) return;
console.log(`src: $src`);

let a = document.querySelector('._open_');

if (!a)
a = document.createElement('a');
[a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

// media container
document.querySelector('._97aPb').appendChild(a);


a.href = src;
;

const addOrUpdate = (selector, observeSelector, attr, mo) =>
let media = document.querySelector(selector);

if (!media) return false;

if (mo)
const obj = ;
obj[attr] = true;
mo.observe(document.querySelector(observeSelector), obj);


setTimeout(() => addOrUpdateMediaLink(media.src), 50);

return true;
;

const createMediaLink = () => ;

createMediaLink();





share|improve this answer



















  • 1




    wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
    – t3chb0t
    Jun 9 at 17:40







  • 1




    The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
    – t3chb0t
    Jun 9 at 17:42







  • 1




    I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
    – t3chb0t
    Jun 9 at 18:47











  • Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
    – Przemek
    Jun 10 at 13:21

















up vote
1
down vote













As is evidenced by the original version of the extension being broken by the HTML update, one drawback of this type of code is that it is brittle. Instagram could update the HTML of their site at anytime, leaving the extension broken. A different approach might be wise. For example, the code could look for video elements - while it is possible to have more than one on a page, the Instagram pages that I looked at appear to either only have one or no video elements. If there are none, then look at the images. Obviously there will likely be more than one image on the page (since the logo and other icons will be present). I'm not sure what the best approach to find the image in focus (maybe see if it has a parent element that is the <article> element, though that also would be coupled to the structure) but I guess one could look for the image with the largest dimensions.



But if you want to stick with the current approach, the rewritten code in Przemek's answer looks more succint and uniform with ecmascript-6. There are a few other suggestions below that might improve things.




You could use an IIFE to wrap all the code up. While the extension cannot use variables or functions defined by web pages or by other content scripts1, it would be a good practice not to add functions that could have the same name as one in the original source code. While it would be unlikely that the Instagram scripts would have a function called createMediaLink, it is possible. And it could also be used to inject certain global variables- e.g. document, window.



;(doc => 
//addOrUpdateMediaLink, etc.

//contents from crateMediaLink,

)(document);



cutOutTheMiddleman



The function passed to setTimeout could be a partial-function, which would eliminate the extra function call. For example, the following block from addOrUpdateImageLink:




setTimeout(() => 
addOrUpdateMediaLink(image.src);
, 50);



Could be simplified like this:



setTimeout(addOrUpdateMediaLink.bind(null, image.src), 50); 


Cool magic, right? (∩`-´)⊃━☆゚.*・。゚




Inside addOrUpdateImageLink there is a new variable created:




var div = document.getElementsByClassName("rQDP3");



Some JS purists might argue that const should be used instead of var, since that is never re-assigned. And also, the naming might be a little misleading, since that function returns an HTMLCollection, so divs would be more appropraite. The same is true for let image = document.getElementsByClassName('FFVAD'); in addOrUpdateImageLink() - since multiple elements are returned images would be more appropriate.




1https://dev.opera.com/extensions/content-scripts/






share|improve this answer























  • Even more magic! But I love it and the links will make the study easier :-)
    – t3chb0t
    Jun 11 at 18:20










  • The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
    – t3chb0t
    Jun 13 at 16:09











Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196163%2fcreating-and-updating-link-to-instagram-image-or-video%23new-answer', 'question_page');

);

Post as a guest






























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
4
down vote



accepted











  • addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;

  • In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;

  • Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;

  • Use strict equality operator (===) wherever possible — it performs no type conversion;

  • Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.

Rewrite



const addOrUpdateMediaLink = src => 
if (!src) return;
console.log(`src: $src`);

let a = document.querySelector('._open_');

if (!a)
a = document.createElement('a');
[a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

// media container
document.querySelector('._97aPb').appendChild(a);


a.href = src;
;

const addOrUpdate = (selector, observeSelector, attr, mo) =>
let media = document.querySelector(selector);

if (!media) return false;

if (mo)
const obj = ;
obj[attr] = true;
mo.observe(document.querySelector(observeSelector), obj);


setTimeout(() => addOrUpdateMediaLink(media.src), 50);

return true;
;

const createMediaLink = () => ;

createMediaLink();





share|improve this answer



















  • 1




    wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
    – t3chb0t
    Jun 9 at 17:40







  • 1




    The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
    – t3chb0t
    Jun 9 at 17:42







  • 1




    I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
    – t3chb0t
    Jun 9 at 18:47











  • Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
    – Przemek
    Jun 10 at 13:21














up vote
4
down vote



accepted











  • addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;

  • In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;

  • Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;

  • Use strict equality operator (===) wherever possible — it performs no type conversion;

  • Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.

Rewrite



const addOrUpdateMediaLink = src => 
if (!src) return;
console.log(`src: $src`);

let a = document.querySelector('._open_');

if (!a)
a = document.createElement('a');
[a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

// media container
document.querySelector('._97aPb').appendChild(a);


a.href = src;
;

const addOrUpdate = (selector, observeSelector, attr, mo) =>
let media = document.querySelector(selector);

if (!media) return false;

if (mo)
const obj = ;
obj[attr] = true;
mo.observe(document.querySelector(observeSelector), obj);


setTimeout(() => addOrUpdateMediaLink(media.src), 50);

return true;
;

const createMediaLink = () => ;

createMediaLink();





share|improve this answer



















  • 1




    wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
    – t3chb0t
    Jun 9 at 17:40







  • 1




    The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
    – t3chb0t
    Jun 9 at 17:42







  • 1




    I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
    – t3chb0t
    Jun 9 at 18:47











  • Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
    – Przemek
    Jun 10 at 13:21












up vote
4
down vote



accepted







up vote
4
down vote



accepted







  • addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;

  • In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;

  • Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;

  • Use strict equality operator (===) wherever possible — it performs no type conversion;

  • Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.

Rewrite



const addOrUpdateMediaLink = src => 
if (!src) return;
console.log(`src: $src`);

let a = document.querySelector('._open_');

if (!a)
a = document.createElement('a');
[a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

// media container
document.querySelector('._97aPb').appendChild(a);


a.href = src;
;

const addOrUpdate = (selector, observeSelector, attr, mo) =>
let media = document.querySelector(selector);

if (!media) return false;

if (mo)
const obj = ;
obj[attr] = true;
mo.observe(document.querySelector(observeSelector), obj);


setTimeout(() => addOrUpdateMediaLink(media.src), 50);

return true;
;

const createMediaLink = () => ;

createMediaLink();





share|improve this answer
















  • addOrUpdateImageLink() and addOrUpdateVideoLink() are essentially the same, therefore it would be better to create just one function that would accept different parameters dependent on whether our media is an image or video;

  • In createMediaLink() you invoke addOrUpdate………Link() functions before you declare them. It is not wrong, but it worsens readability and code flow;

  • Setting 50 ms delay is not the best way to achieve what you are trying to do. Since I don't know how the DOM workings look like, I left it unchanged though;

  • Use strict equality operator (===) wherever possible — it performs no type conversion;

  • Once you pick which quotes you use ('' or ""), stick to it. Generally, single quotes option is more popular and standard.

Rewrite



const addOrUpdateMediaLink = src => 
if (!src) return;
console.log(`src: $src`);

let a = document.querySelector('._open_');

if (!a)
a = document.createElement('a');
[a.className, a.textContent, a.target] = ['_open_', 'Open in new tab', '_blank'];

// media container
document.querySelector('._97aPb').appendChild(a);


a.href = src;
;

const addOrUpdate = (selector, observeSelector, attr, mo) =>
let media = document.querySelector(selector);

if (!media) return false;

if (mo)
const obj = ;
obj[attr] = true;
mo.observe(document.querySelector(observeSelector), obj);


setTimeout(() => addOrUpdateMediaLink(media.src), 50);

return true;
;

const createMediaLink = () => ;

createMediaLink();






share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 10 at 13:22


























answered Jun 9 at 17:06









Przemek

1,032213




1,032213







  • 1




    wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
    – t3chb0t
    Jun 9 at 17:40







  • 1




    The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
    – t3chb0t
    Jun 9 at 17:42







  • 1




    I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
    – t3chb0t
    Jun 9 at 18:47











  • Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
    – Przemek
    Jun 10 at 13:21












  • 1




    wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
    – t3chb0t
    Jun 9 at 17:40







  • 1




    The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
    – t3chb0t
    Jun 9 at 17:42







  • 1




    I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
    – t3chb0t
    Jun 9 at 18:47











  • Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
    – Przemek
    Jun 10 at 13:21







1




1




wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
– t3chb0t
Jun 9 at 17:40





wow, this looks awsome! I hadn't thought there would be possbile so many tricks :-)
– t3chb0t
Jun 9 at 17:40





1




1




The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
– t3chb0t
Jun 9 at 17:42





The 50ms is just a small randomly picked number because without it the link was often empty when you switched to the next image because instagram is rebuilding an element tree that wasn't always complete.
– t3chb0t
Jun 9 at 17:42





1




1




I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
– t3chb0t
Jun 9 at 18:47





I had to make one fix. This didn't work return addOrUpdate(mo, ...params); so I changed it to return addOrUpdate(mo, ...selectors['img'], 'childList') || addOrUpdate(mo, ...selectors['video'], 'attributes'); - but maybe it's just me who isn't entirely understanding the new syntax yet because in order to learn it didn't just copy paste it but wrote it on my own so I might have made a mistake somewhere ;-)
– t3chb0t
Jun 9 at 18:47













Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
– Przemek
Jun 10 at 13:21




Yeah, I was in a hurry when I wrote this. Now, after giving it one more second, I better understood what's going on. I've updated the code in my answer.
– Przemek
Jun 10 at 13:21












up vote
1
down vote













As is evidenced by the original version of the extension being broken by the HTML update, one drawback of this type of code is that it is brittle. Instagram could update the HTML of their site at anytime, leaving the extension broken. A different approach might be wise. For example, the code could look for video elements - while it is possible to have more than one on a page, the Instagram pages that I looked at appear to either only have one or no video elements. If there are none, then look at the images. Obviously there will likely be more than one image on the page (since the logo and other icons will be present). I'm not sure what the best approach to find the image in focus (maybe see if it has a parent element that is the <article> element, though that also would be coupled to the structure) but I guess one could look for the image with the largest dimensions.



But if you want to stick with the current approach, the rewritten code in Przemek's answer looks more succint and uniform with ecmascript-6. There are a few other suggestions below that might improve things.




You could use an IIFE to wrap all the code up. While the extension cannot use variables or functions defined by web pages or by other content scripts1, it would be a good practice not to add functions that could have the same name as one in the original source code. While it would be unlikely that the Instagram scripts would have a function called createMediaLink, it is possible. And it could also be used to inject certain global variables- e.g. document, window.



;(doc => 
//addOrUpdateMediaLink, etc.

//contents from crateMediaLink,

)(document);



cutOutTheMiddleman



The function passed to setTimeout could be a partial-function, which would eliminate the extra function call. For example, the following block from addOrUpdateImageLink:




setTimeout(() => 
addOrUpdateMediaLink(image.src);
, 50);



Could be simplified like this:



setTimeout(addOrUpdateMediaLink.bind(null, image.src), 50); 


Cool magic, right? (∩`-´)⊃━☆゚.*・。゚




Inside addOrUpdateImageLink there is a new variable created:




var div = document.getElementsByClassName("rQDP3");



Some JS purists might argue that const should be used instead of var, since that is never re-assigned. And also, the naming might be a little misleading, since that function returns an HTMLCollection, so divs would be more appropraite. The same is true for let image = document.getElementsByClassName('FFVAD'); in addOrUpdateImageLink() - since multiple elements are returned images would be more appropriate.




1https://dev.opera.com/extensions/content-scripts/






share|improve this answer























  • Even more magic! But I love it and the links will make the study easier :-)
    – t3chb0t
    Jun 11 at 18:20










  • The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
    – t3chb0t
    Jun 13 at 16:09















up vote
1
down vote













As is evidenced by the original version of the extension being broken by the HTML update, one drawback of this type of code is that it is brittle. Instagram could update the HTML of their site at anytime, leaving the extension broken. A different approach might be wise. For example, the code could look for video elements - while it is possible to have more than one on a page, the Instagram pages that I looked at appear to either only have one or no video elements. If there are none, then look at the images. Obviously there will likely be more than one image on the page (since the logo and other icons will be present). I'm not sure what the best approach to find the image in focus (maybe see if it has a parent element that is the <article> element, though that also would be coupled to the structure) but I guess one could look for the image with the largest dimensions.



But if you want to stick with the current approach, the rewritten code in Przemek's answer looks more succint and uniform with ecmascript-6. There are a few other suggestions below that might improve things.




You could use an IIFE to wrap all the code up. While the extension cannot use variables or functions defined by web pages or by other content scripts1, it would be a good practice not to add functions that could have the same name as one in the original source code. While it would be unlikely that the Instagram scripts would have a function called createMediaLink, it is possible. And it could also be used to inject certain global variables- e.g. document, window.



;(doc => 
//addOrUpdateMediaLink, etc.

//contents from crateMediaLink,

)(document);



cutOutTheMiddleman



The function passed to setTimeout could be a partial-function, which would eliminate the extra function call. For example, the following block from addOrUpdateImageLink:




setTimeout(() => 
addOrUpdateMediaLink(image.src);
, 50);



Could be simplified like this:



setTimeout(addOrUpdateMediaLink.bind(null, image.src), 50); 


Cool magic, right? (∩`-´)⊃━☆゚.*・。゚




Inside addOrUpdateImageLink there is a new variable created:




var div = document.getElementsByClassName("rQDP3");



Some JS purists might argue that const should be used instead of var, since that is never re-assigned. And also, the naming might be a little misleading, since that function returns an HTMLCollection, so divs would be more appropraite. The same is true for let image = document.getElementsByClassName('FFVAD'); in addOrUpdateImageLink() - since multiple elements are returned images would be more appropriate.




1https://dev.opera.com/extensions/content-scripts/






share|improve this answer























  • Even more magic! But I love it and the links will make the study easier :-)
    – t3chb0t
    Jun 11 at 18:20










  • The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
    – t3chb0t
    Jun 13 at 16:09













up vote
1
down vote










up vote
1
down vote









As is evidenced by the original version of the extension being broken by the HTML update, one drawback of this type of code is that it is brittle. Instagram could update the HTML of their site at anytime, leaving the extension broken. A different approach might be wise. For example, the code could look for video elements - while it is possible to have more than one on a page, the Instagram pages that I looked at appear to either only have one or no video elements. If there are none, then look at the images. Obviously there will likely be more than one image on the page (since the logo and other icons will be present). I'm not sure what the best approach to find the image in focus (maybe see if it has a parent element that is the <article> element, though that also would be coupled to the structure) but I guess one could look for the image with the largest dimensions.



But if you want to stick with the current approach, the rewritten code in Przemek's answer looks more succint and uniform with ecmascript-6. There are a few other suggestions below that might improve things.




You could use an IIFE to wrap all the code up. While the extension cannot use variables or functions defined by web pages or by other content scripts1, it would be a good practice not to add functions that could have the same name as one in the original source code. While it would be unlikely that the Instagram scripts would have a function called createMediaLink, it is possible. And it could also be used to inject certain global variables- e.g. document, window.



;(doc => 
//addOrUpdateMediaLink, etc.

//contents from crateMediaLink,

)(document);



cutOutTheMiddleman



The function passed to setTimeout could be a partial-function, which would eliminate the extra function call. For example, the following block from addOrUpdateImageLink:




setTimeout(() => 
addOrUpdateMediaLink(image.src);
, 50);



Could be simplified like this:



setTimeout(addOrUpdateMediaLink.bind(null, image.src), 50); 


Cool magic, right? (∩`-´)⊃━☆゚.*・。゚




Inside addOrUpdateImageLink there is a new variable created:




var div = document.getElementsByClassName("rQDP3");



Some JS purists might argue that const should be used instead of var, since that is never re-assigned. And also, the naming might be a little misleading, since that function returns an HTMLCollection, so divs would be more appropraite. The same is true for let image = document.getElementsByClassName('FFVAD'); in addOrUpdateImageLink() - since multiple elements are returned images would be more appropriate.




1https://dev.opera.com/extensions/content-scripts/






share|improve this answer















As is evidenced by the original version of the extension being broken by the HTML update, one drawback of this type of code is that it is brittle. Instagram could update the HTML of their site at anytime, leaving the extension broken. A different approach might be wise. For example, the code could look for video elements - while it is possible to have more than one on a page, the Instagram pages that I looked at appear to either only have one or no video elements. If there are none, then look at the images. Obviously there will likely be more than one image on the page (since the logo and other icons will be present). I'm not sure what the best approach to find the image in focus (maybe see if it has a parent element that is the <article> element, though that also would be coupled to the structure) but I guess one could look for the image with the largest dimensions.



But if you want to stick with the current approach, the rewritten code in Przemek's answer looks more succint and uniform with ecmascript-6. There are a few other suggestions below that might improve things.




You could use an IIFE to wrap all the code up. While the extension cannot use variables or functions defined by web pages or by other content scripts1, it would be a good practice not to add functions that could have the same name as one in the original source code. While it would be unlikely that the Instagram scripts would have a function called createMediaLink, it is possible. And it could also be used to inject certain global variables- e.g. document, window.



;(doc => 
//addOrUpdateMediaLink, etc.

//contents from crateMediaLink,

)(document);



cutOutTheMiddleman



The function passed to setTimeout could be a partial-function, which would eliminate the extra function call. For example, the following block from addOrUpdateImageLink:




setTimeout(() => 
addOrUpdateMediaLink(image.src);
, 50);



Could be simplified like this:



setTimeout(addOrUpdateMediaLink.bind(null, image.src), 50); 


Cool magic, right? (∩`-´)⊃━☆゚.*・。゚




Inside addOrUpdateImageLink there is a new variable created:




var div = document.getElementsByClassName("rQDP3");



Some JS purists might argue that const should be used instead of var, since that is never re-assigned. And also, the naming might be a little misleading, since that function returns an HTMLCollection, so divs would be more appropraite. The same is true for let image = document.getElementsByClassName('FFVAD'); in addOrUpdateImageLink() - since multiple elements are returned images would be more appropriate.




1https://dev.opera.com/extensions/content-scripts/







share|improve this answer















share|improve this answer



share|improve this answer








edited Jun 11 at 18:25


























answered Jun 11 at 17:12









Sam Onela

5,76961543




5,76961543











  • Even more magic! But I love it and the links will make the study easier :-)
    – t3chb0t
    Jun 11 at 18:20










  • The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
    – t3chb0t
    Jun 13 at 16:09

















  • Even more magic! But I love it and the links will make the study easier :-)
    – t3chb0t
    Jun 11 at 18:20










  • The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
    – t3chb0t
    Jun 13 at 16:09
















Even more magic! But I love it and the links will make the study easier :-)
– t3chb0t
Jun 11 at 18:20




Even more magic! But I love it and the links will make the study easier :-)
– t3chb0t
Jun 11 at 18:20












The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
– t3chb0t
Jun 13 at 16:09





The idea with injecting global variable is nice but unfortuantelly extensions are running in a sendbox and they don't have access to any custom javascript variables e.g. on the window object so even when injected like in your example it's not possible to pass them to the script :-( there is one very useful window._sharedData that contains all the blob: viedeo links but the extension does not see it.
– t3chb0t
Jun 13 at 16:09













 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f196163%2fcreating-and-updating-link-to-instagram-image-or-video%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?