ฉันจะอ้างอิงแท็กสคริปต์ที่โหลดสคริปต์ที่กำลังเรียกใช้งานอยู่ได้อย่างไร


303

ฉันจะอ้างอิงองค์ประกอบของสคริปต์ที่โหลดจาวาสคริปต์ที่กำลังทำงานอยู่ได้อย่างไร

นี่คือสถานการณ์ ฉันมีสคริปต์ "ปรมาจารย์" ที่โหลดสูงในหน้าแรกสิ่งแรกภายใต้แท็ก HEAD

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script type="text/javascript" src="scripts.js"></script>

มีสคริปต์ใน "scripts.js" ซึ่งจำเป็นต้องสามารถโหลดสคริปต์อื่นตามความต้องการได้ วิธีการปกติไม่ได้ผลสำหรับฉันเพราะฉันต้องการเพิ่มสคริปต์ใหม่โดยไม่อ้างอิงแท็ก HEAD เนื่องจากองค์ประกอบ HEAD ยังแสดงผลไม่เสร็จ:

document.getElementsByTagName('head')[0].appendChild(v);

สิ่งที่ฉันต้องการทำคือการอ้างอิงองค์ประกอบของสคริปต์ที่โหลดสคริปต์ปัจจุบันเพื่อให้ฉันสามารถผนวกแท็กสคริปต์ที่โหลดใหม่แบบไดนามิกของฉันลงใน DOM หลังจากนั้น

<script type="text/javascript" src="scripts.js"></script>
loaded by scripts.js--><script type="text/javascript" src="new_script1.js"></script>
loaded by scripts.js --><script type="text/javascript" src="new_script2.js"></script>

1
คำเตือน: การปรับเปลี่ยน DOM ในขณะที่มันยังคงโหลดจะทำให้คุณโลกของการบาดเจ็บใน IE6 และ IE7 คุณจะต้องใช้รหัสนั้นดีกว่าหลังจากโหลดหน้าเว็บ
Triptych

6
ดูเหมือนว่ามันจะขึ้นอยู่กับ caniuse ในขณะนี้: caniuse.com/#feat=document-currentscript
Tyler

คำตอบ:


653

วิธีรับองค์ประกอบสคริปต์ปัจจุบัน:

1. การใช้งาน document.currentScript

document.currentScriptจะคืน<script>องค์ประกอบที่สคริปต์กำลังดำเนินการอยู่

<script>
var me = document.currentScript;
</script>

ประโยชน์ที่ได้รับ

  • ง่ายและชัดเจน น่าเชื่อถือ
  • ไม่จำเป็นต้องแก้ไขแท็กสคริปต์
  • ทำงานร่วมกับสคริปต์แบบอะซิงโครนัส ( defer& async)
  • ทำงานร่วมกับสคริปต์ที่แทรกแบบไดนามิก

ปัญหาที่เกิดขึ้น

  • จะไม่ทำงานในเบราว์เซอร์ที่เก่ากว่าและ IE
  • ไม่ทำงานกับโมดูล <script type="module">

2. เลือกสคริปต์ตามรหัส

ให้สคริปต์แอตทริบิวต์ ID จะช่วยให้คุณสามารถเลือกของคุณ id document.getElementById()จากภายในโดยใช้

<script id="myscript">
var me = document.getElementById('myscript');
</script>

ประโยชน์ที่ได้รับ

  • ง่ายและชัดเจน น่าเชื่อถือ
  • รองรับเกือบทุกระดับ
  • ทำงานร่วมกับสคริปต์แบบอะซิงโครนัส ( defer& async)
  • ทำงานร่วมกับสคริปต์ที่แทรกแบบไดนามิก

ปัญหาที่เกิดขึ้น

  • ต้องเพิ่มแอตทริบิวต์ที่กำหนดเองลงในแท็กสคริปต์
  • id คุณลักษณะอาจทำให้พฤติกรรมแปลก ๆ สำหรับสคริปต์ในเบราว์เซอร์บางกรณีขอบบาง

3. เลือกสคริปต์โดยใช้data-*แอตทริบิวต์

ให้สคริปต์data-*คุณลักษณะที่จะช่วยให้คุณสามารถเลือกได้อย่างง่ายดายจากภายใน

<script data-name="myscript">
var me = document.querySelector('script[data-name="myscript"]');
</script>

สิ่งนี้มีประโยชน์น้อยกว่าตัวเลือกก่อนหน้า

ประโยชน์ที่ได้รับ

  • ง่ายและชัดเจน
  • ทำงานร่วมกับสคริปต์แบบอะซิงโครนัส ( defer& async)
  • ทำงานร่วมกับสคริปต์ที่แทรกแบบไดนามิก

ปัญหาที่เกิดขึ้น

  • ต้องเพิ่มแอตทริบิวต์ที่กำหนดเองลงในแท็กสคริปต์
  • HTML5 และquerySelector()ไม่เข้ากันได้กับทุกเบราว์เซอร์
  • สนับสนุนอย่างกว้างขวางน้อยกว่าการใช้idแอตทริบิวต์
  • จะได้รับรอบ<script>กับidกรณีขอบ
  • อาจสับสนหากองค์ประกอบอื่นมีข้อมูลและค่าเดียวกันในหน้า

4. เลือกสคริปต์ตาม src

แทนที่จะใช้คุณลักษณะข้อมูลคุณสามารถใช้ตัวเลือกเพื่อเลือกสคริปต์ตามแหล่งที่มา:

<script src="//example.com/embed.js"></script>

ใน embed.js:

var me = document.querySelector('script[src="//example.com/embed.js"]');

ประโยชน์ที่ได้รับ

  • น่าเชื่อถือ
  • ทำงานร่วมกับสคริปต์แบบอะซิงโครนัส ( defer& async)
  • ทำงานร่วมกับสคริปต์ที่แทรกแบบไดนามิก
  • ไม่จำเป็นต้องมีแอตทริบิวต์หรือรหัสที่กำหนดเอง

ปัญหาที่เกิดขึ้น

  • ไม่ได้ทำงานให้กับสคริปต์ท้องถิ่น
  • จะทำให้เกิดปัญหาในสภาพแวดล้อมต่าง ๆ เช่นการพัฒนาและการผลิต
  • คงที่และเปราะบาง การเปลี่ยนตำแหน่งของไฟล์สคริปต์จะต้องมีการแก้ไขสคริปต์
  • สนับสนุนอย่างกว้างขวางน้อยกว่าการใช้idแอตทริบิวต์
  • จะทำให้เกิดปัญหาหากคุณโหลดสคริปต์เดียวกันสองครั้ง

5. วนรอบสคริปต์ทั้งหมดเพื่อค้นหาสิ่งที่คุณต้องการ

นอกจากนี้เรายังสามารถวนรอบทุกองค์ประกอบของสคริปต์และตรวจสอบแต่ละรายการเพื่อเลือกองค์ประกอบที่เราต้องการ:

<script>
var me = null;
var scripts = document.getElementsByTagName("script")
for (var i = 0; i < scripts.length; ++i) {
    if( isMe(scripts[i])){
      me = scripts[i];
    }
}
</script>

วิธีนี้ทำให้เราสามารถใช้ทั้งเทคนิคก่อนหน้านี้ในเบราว์เซอร์รุ่นเก่าที่ไม่รองรับquerySelector()คุณสมบัติต่างๆ ตัวอย่างเช่น:

function isMe(scriptElem){
    return scriptElem.getAttribute('src') === "//example.com/embed.js";
}

สิ่งนี้ได้รับประโยชน์และปัญหาของวิธีการใดก็ตามที่ได้รับมา แต่ไม่พึ่งพาquerySelector()ดังนั้นจะทำงานในเบราว์เซอร์รุ่นเก่า

6. รับสคริปต์ที่ดำเนินการครั้งสุดท้าย

เนื่องจากสคริปต์ถูกเรียกใช้งานตามลำดับองค์ประกอบสคริปต์ล่าสุดมักจะเป็นสคริปต์ที่ทำงานอยู่ในปัจจุบัน:

<script>
var scripts = document.getElementsByTagName( 'script' );
var me = scripts[ scripts.length - 1 ];
</script>

ประโยชน์ที่ได้รับ

  • ง่าย
  • รองรับเกือบทุกระดับ
  • ไม่จำเป็นต้องมีแอตทริบิวต์หรือรหัสที่กำหนดเอง

ปัญหาที่เกิดขึ้น

  • ไม่ได้ทำงานกับสคริปต์ไม่ตรงกัน ( defer& async)
  • ไม่ได้ทำงานกับสคริปต์แทรกแบบไดนามิก

4
นี่ควรเป็นคำตอบ
Royi Namir

1
เห็นด้วยกับ @RoyiNamir นี่คือคำตอบที่ดีที่สุด
ฮันส์

27
ขอบคุณพวกคุณ แต่คุณรู้ว่าฉันตอบ 4 ปีหลังจากคำตอบที่ยอมรับใช่มั้ย :)
ราคา

5
"document.currentScript" ไม่ทำงานสำหรับฉันด้วยสคริปต์ที่โหลดแบบไดนามิกส่งคืน null ใน chrome / firefox ล่าสุด "สคริปต์ที่เรียกใช้งานครั้งสุดท้าย" ใช้งานได้
xwild

1
ไม่ทำงานเมื่อscriptอยู่ในการtemplateแทรกใน Shadow DOM
Supersharp

86

เนื่องจากสคริปต์จะถูกดำเนินการตามลำดับแท็กสคริปต์ที่ดำเนินการในปัจจุบันจะเป็นแท็กสคริปต์สุดท้ายเสมอบนหน้าเว็บจนกว่าจะถึงตอนนั้น ดังนั้นในการรับแท็กสคริปต์คุณสามารถทำได้:

var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];

3
ง่ายและสง่างาม มีตัวอย่างอยู่ใน Google Charts / Visualizations API ใหม่หากคุณแกะจาวาสคริปต์ พวกเขาโหลดข้อมูล JSON จากภายในแท็กสคริปต์ดู: ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js
Jason Thrasher

1
นี่เป็นความคิดที่ดีมากและใช้งานได้ตามปกติสำหรับฉัน แต่ฉันควรเพิ่มว่ามีบางครั้งที่ฉันพบว่ามันส่งคืนการอ้างอิงไปยังสคริปต์อื่น ไม่แน่ใจว่าทำไม - ไม่สามารถติดตามสิ่งนั้นได้ ดังนั้นฉันมักจะใช้วิธีอื่นเช่นฉันเขียนโค้ดชื่อไฟล์สคริปต์และหาแท็กสคริปต์ด้วยชื่อไฟล์นั้น
Ken Smith

53
อินสแตนซ์หนึ่งที่ฉันนึกได้ว่าตำแหน่งนี้อาจส่งคืนผลลัพธ์ที่ไม่ถูกต้องคือเมื่อเพิ่มแท็กสคริปต์ใน DOM แบบอะซิงโครนัส
Coffee Bite

9
ใช่สิ่งนี้สามารถมีผลลัพธ์ที่คาดเดาไม่ได้ดังนั้นคุณสามารถลองใช้ตัวเลือกแทน: $ ('script [src * = "/ mysource.js"]') ???
Jason Sebring

7
มันไม่ทำงานเมื่อคุณโหลดสคริปต์หลังจากโหลดหน้าเว็บแล้ว คุณอาจจะไม่ได้รับแท็กที่ถูกต้อง
ThemeZ

11

น่าจะเป็นสิ่งที่ง่ายที่สุดที่จะทำจะให้แท็กใบของคุณidแอตทริบิวต์


2
แม้ว่าคุณจะถูกต้อง แต่ก็มีหลายกรณีที่คำถามของ OP นั้นถูกต้องสองสามข้อคือ 1) เมื่อคุณคลาน 2) เมื่อคุณทำงานกับ DOM ของลูกค้าและเขาไม่ต้องการเปลี่ยนแปลง
nichochar

10

สคริปต์ถูกเรียกใช้งานตามลำดับหากไม่มีแอ็ตทริบิวต์ "defer" หรือ "async" การรู้หนึ่งในคุณลักษณะ ID / SRC / TITLE ที่เป็นไปได้ของแท็กสคริปต์อาจทำงานได้ในกรณีเหล่านั้น ดังนั้นคำแนะนำทั้ง Greg และ Justin นั้นถูกต้อง

มีข้อเสนอสำหรับdocument.currentScriptรายการ WHATWG แล้ว

แก้ไข : Firefox> 4 ใช้คุณสมบัติที่มีประโยชน์มากแล้ว แต่ไม่สามารถใช้ได้ใน IE11 ครั้งล่าสุดที่ฉันตรวจสอบและมีเฉพาะใน Chrome 29 และ Safari 8

แก้ไข : ไม่มีใครพูดถึงคอลเลกชัน "document.scripts" แต่ฉันเชื่อว่าต่อไปนี้อาจเป็นทางเลือกเบราว์เซอร์ข้ามที่ดีในการรับสคริปต์ที่กำลังทำงานอยู่:

var me = document.scripts[document.scripts.length -1];

1
It's document.scripts ไม่ใช่ document.script
Moritz

9

นี่คือ polyfill เล็กน้อยที่ยกระดับdocument.CurrentScriptหากมีอยู่และกลับไปหาสคริปต์ตาม ID

<script id="uniqueScriptId">
    (function () {
        var thisScript = document.CurrentScript || document.getElementByID('uniqueScriptId');

        // your code referencing thisScript here
    ());
</script>

หากคุณรวมสิ่งนี้ไว้ที่ด้านบนสุดของแท็กสคริปต์ทั้งหมดฉันเชื่อว่าคุณจะสามารถรู้ได้อย่างสม่ำเสมอว่าแท็กสคริปต์ใดถูกเปิดใช้งานและคุณจะสามารถอ้างอิงแท็กสคริปต์ในบริบทของการโทรกลับแบบอะซิงโครนัส

ยังไม่ได้ทดสอบโปรดแสดงความคิดเห็นกับผู้อื่นหากคุณลอง


idแอตทริบิวต์ที่ไม่ถูกต้องในscriptองค์ประกอบแม้ว่า ปัญหานี้สามารถก่อให้เกิดปัญหาอะไรได้บ้าง
nr

1
@nr - ไม่ องค์ประกอบทั้งหมดสามารถมีidแอตทริบิวต์ได้ id,, classและslotถูกกำหนดที่ระดับ DOM ไม่ใช่ระดับ HTML หากคุณไปที่คุณลักษณะทั่วโลกใน HTMLและเลื่อนรายการคุณจะพบ"มาตรฐาน DOM กำหนดข้อกำหนดตัวแทนผู้ใช้สำหรับคลาสรหัสและคุณสมบัติสล็อตสำหรับองค์ประกอบใด ๆ ในเนมสเปซใด ๆ " ตามด้วย"คลาสรหัสและแอตทริบิวต์ของสล็อตอาจระบุในองค์ประกอบ HTML ทั้งหมด" พระข้อมูลจำเพาะครอบคลุมมันนี่
TJ Crowder

6

จะต้องทำงานเมื่อโหลดหน้าเว็บและเมื่อเพิ่มแท็กสคริปต์ด้วย javascript (เช่น ajax)

<script id="currentScript">
var $this = document.getElementById("currentScript");
$this.setAttribute("id","");
//...
</script>

3

ทำตามขั้นตอนง่าย ๆ เหล่านี้เพื่อรับการอ้างอิงถึงบล็อกสคริปต์ที่กำลังเรียกใช้งานปัจจุบัน:

  1. ใส่สตริงที่ไม่ซ้ำแบบสุ่มบางอย่างในบล็อกสคริปต์ (ต้องไม่ซ้ำกัน / แตกต่างกันในแต่ละบล็อกสคริปต์)
  2. ทำซ้ำผลลัพธ์ของ document.getElementsByTagName ('script') ค้นหาสตริงที่ไม่ซ้ำจากแต่ละเนื้อหา (ได้มาจากคุณสมบัติ InnerText / textContent)

ตัวอย่าง (ABCDE345678 เป็น ID เฉพาะ) :

<script type="text/javascript">
var A=document.getElementsByTagName('script'),i=count(A),thi$;
for(;i;thi$=A[--i])
  if((thi$.innerText||thi$.textContent).indexOf('ABCDE345678'))break;
// Now thi$ is refer to current script block
</script>

btw สำหรับกรณีของคุณคุณสามารถใช้เมธอด document.write () แบบเก่าเพื่อรวมสคริปต์อื่น ตามที่คุณกล่าวถึงว่า DOM ยังไม่ได้แสดงผลคุณสามารถใช้ประโยชน์จากความจริงที่ว่าเบราว์เซอร์รันสคริปต์ตามลำดับเชิงเส้นเสมอ (ยกเว้นที่รอการแสดงผลซึ่งจะแสดงในภายหลัง) ดังนั้นเอกสารที่เหลือยังคงเป็น "ไม่อยู่" สิ่งที่คุณเขียนผ่าน document.write () จะถูกวางไว้หลังสคริปต์ผู้โทร

ตัวอย่างหน้า HTML ดั้งเดิม :

<!doctype html>
<html><head>
<script src="script.js"></script>
<script src="otherscript.js"></script>
<body>anything</body></html>

เนื้อหาของ script.js :

document.write('<script src="inserted.js"></script>');

หลังจากแสดงผลแล้วโครงสร้าง DOM จะกลายเป็น:

HEAD
  SCRIPT script.js
  SCRIPT inserted.js
  SCRIPT otherscript.js
BODY

ดูเหมือนว่าจะใช้ได้เฉพาะกับสคริปต์แบบอินไลน์ไม่ใช่สำหรับสคริปต์ภายนอก ในกรณีหลังคุณสมบัติทั้งหมด innerText, text และ textContent จะว่างเปล่า
Jos de Jong

3

แนวทางในการจัดการกับสคริปต์ async & deferred คือใช้ประโยชน์จาก onload handler- ตั้งค่า onload handler สำหรับแท็กสคริปต์ทั้งหมดและอันแรกที่ควรจะเป็นของคุณ

function getCurrentScript(callback) {
  if (document.currentScript) {
    callback(document.currentScript);
    return;
  }
  var scripts = document.scripts;
  function onLoad() {
    for (var i = 0; i < scripts.length; ++i) {
      scripts[i].removeEventListener('load', onLoad, false);
    }
    callback(event.target);
  }
  for (var i = 0; i < scripts.length; ++i) {
    scripts[i].addEventListener('load', onLoad, false);
  }
}

getCurrentScript(function(currentScript) {
  window.console.log(currentScript.src);
});

3

ในการรับสคริปต์ซึ่งขณะนี้โหลดสคริปต์ที่คุณสามารถใช้ได้

var thisScript = document.currentScript;

คุณต้องทำการอ้างอิงที่จุดเริ่มต้นของสคริปต์ของคุณเพื่อให้คุณสามารถโทรในภายหลัง

var url = thisScript.src

2

พิจารณาอัลกอริทึมนี้ เมื่อสคริปต์ของคุณโหลด (หากมีสคริปต์ที่เหมือนกันหลายรายการ) ให้มองผ่าน document.scripts ค้นหาสคริปต์แรกด้วยแอตทริบิวต์ "src" ที่ถูกต้องและบันทึกไว้และทำเครื่องหมายเป็น 'เยี่ยมชม' ด้วย data-attribute หรือ className ที่ไม่ซ้ำกัน

เมื่อสคริปต์ถัดไปโหลดให้สแกน document.scripts อีกครั้งผ่านสคริปต์ที่ทำเครื่องหมายว่าเยี่ยมชมแล้ว ใช้อินสแตนซ์ที่ไม่ได้เข้าชมครั้งแรกของสคริปต์นั้น

สิ่งนี้จะถือว่าสคริปต์ที่เหมือนกันมีแนวโน้มที่จะดำเนินการตามลำดับที่ถูกโหลดตั้งแต่ส่วนหัวจนถึงส่วนบนจากล่างสู่ล่างจากแบบซิงโครนัสไปยังแบบอะซิงโครนัส

(function () {
  var scripts = document.scripts;

  // Scan for this data-* attribute
  var dataAttr = 'data-your-attribute-here';

  var i = 0;
  var script;
  while (i < scripts.length) {
    script = scripts[i];
    if (/your_script_here\.js/i.test(script.src)
        && !script.hasAttribute(dataAttr)) {

        // A good match will break the loop before
        // script is set to null.
        break;
    }

    // If we exit the loop through a while condition failure,
    // a check for null will reveal there are no matches.
    script = null;
    ++i;
  }

  /**
   * This specific your_script_here.js script tag.
   * @type {Element|Node}
   */
  var yourScriptVariable = null;

  // Mark the script an pass it on.
  if (script) {
    script.setAttribute(dataAttr, '');
    yourScriptVariable = script;
  }
})();

การดำเนินการนี้จะสแกนสคริปต์ทั้งหมดสำหรับสคริปต์จับคู่แรกที่ไม่ได้ทำเครื่องหมายด้วยคุณลักษณะพิเศษ

จากนั้นทำเครื่องหมายโหนดนั้นหากพบว่ามี data-attribute ดังนั้นการสแกนที่ตามมาจะไม่เลือก สิ่งนี้คล้ายกับอัลกอริธึมการสำรวจเส้นทาง BFS และ DFS โดยที่โหนดอาจถูกทำเครื่องหมายเป็น 'เยี่ยมชม' เพื่อป้องกันการกลับมาใหม่


ยินดีต้อนรับสู่ Stack Overflow คุณต้องการรวมรหัสบางส่วนกับอัลกอริทึมหรือไม่
Gary99

ก่อนอื่นคุณจะไป @ Gary99
LSOJ

0

ฉันได้รับสิ่งนี้ซึ่งทำงานใน FF3, IE6 & 7 วิธีการในสคริปต์ที่โหลดตามความต้องการไม่สามารถใช้ได้จนกว่าการโหลดหน้าเว็บจะเสร็จสมบูรณ์ แต่สิ่งนี้ยังมีประโยชน์มาก

//handle on-demand loading of javascripts
makescript = function(url){
    var v = document.createElement('script');
    v.src=url;
    v.type='text/javascript';

    //insertAfter. Get last <script> tag in DOM
    d=document.getElementsByTagName('script')[(document.getElementsByTagName('script').length-1)];
    d.parentNode.insertBefore( v, d.nextSibling );
}

0

หากคุณสามารถสมมติชื่อไฟล์ของสคริปต์คุณสามารถค้นหาได้ ฉันทดสอบเฉพาะฟังก์ชั่นต่อไปนี้ใน Firefox เท่านั้นจริงๆ

  function findMe(tag, attr, file) {
    var tags = document.getElementsByTagName(tag);
    var r = new RegExp(file + '$');
    for (var i = 0;i < tags.length;i++) {
      if (r.exec(tags[i][attr])) {
        return tags[i][attr];
      }
    }
  };
  var element = findMe('script', 'src', 'scripts.js');

1
ล้าสมัยมาก สิ่งนี้สามารถทำได้ด้วยการสอบถาม selector, oneliner ง่าย ๆ !
Fabian von Ellerts

-1

ฉันพบว่ารหัสต่อไปนี้เป็นรหัสที่สอดคล้องกันมากที่สุดนักแสดงและเรียบง่าย

var scripts = document.getElementsByTagName('script');
var thisScript = null;
var i = scripts.length;
while (i--) {
  if (scripts[i].src && (scripts[i].src.indexOf('yourscript.js') !== -1)) {
    thisScript = scripts[i];
    break;
  }
}
console.log(thisScript);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.